diff options
author | Ben Murdoch <benm@google.com> | 2013-07-10 11:40:50 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2013-07-10 11:40:50 +0100 |
commit | eb525c5499e34cc9c4b825d6d9e75bb07cc06ace (patch) | |
tree | d908ce4bfe1717d2cd53f41327d8b9ba8304355f /native_client_sdk | |
parent | 3c54152607de4272b3da0c146b71dcba8a0e5610 (diff) | |
download | chromium_org-eb525c5499e34cc9c4b825d6d9e75bb07cc06ace.tar.gz |
Merge from Chromium at DEPS revision r210036
This commit was generated by merge_to_master.py.
Change-Id: Ib0e33a83ad5dfa541481e83d7acfc6970e68f471
Diffstat (limited to 'native_client_sdk')
181 files changed, 7620 insertions, 6345 deletions
diff --git a/native_client_sdk/OWNERS b/native_client_sdk/OWNERS index 9cd1776b7d..2f918b8434 100644 --- a/native_client_sdk/OWNERS +++ b/native_client_sdk/OWNERS @@ -3,3 +3,4 @@ noelallen@chromium.org erikkay@chromium.org binji@chromium.org sbc@chromium.org +nfullagar@chromium.org diff --git a/native_client_sdk/src/README b/native_client_sdk/src/README index c85406fa01..82503b9a83 100644 --- a/native_client_sdk/src/README +++ b/native_client_sdk/src/README @@ -1,8 +1,10 @@ -Welcome to the Native Client SDK. +Welcome to the Native Client SDK +================================ Native Client Tools Bundle Version: ${VERSION} -Revision: ${REVISION} +Chrome Revision: ${CHROME_REVISION} +Native Client Revision: ${NACL_REVISION} Build Date: ${DATE} Please refer to the online documentation here: @@ -10,6 +12,7 @@ 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, please read the online documentation on contributing code to Chromium here: @@ -17,6 +20,7 @@ online documentation on contributing code to Chromium here: http://www.chromium.org/developers/contributing-code KNOWN ISSUES +------------ Please refer to the online documentation here: diff --git a/native_client_sdk/src/README.Makefiles b/native_client_sdk/src/README.Makefiles new file mode 100644 index 0000000000..117939561e --- /dev/null +++ b/native_client_sdk/src/README.Makefiles @@ -0,0 +1,95 @@ +Build System for Native Client SDK examples +=========================================== + +The examples and libraries that ship with the Native Client SDK use a +build system based on GNU Make. + +Each example or library is contained in its own directory along with a +Makefile. The Makefiles are capable of building Native Client +applications and libraries using any of the available toolchains as well +as building host applications with the host's toolchain. In order to +keep the top-level Makefiles simple, most of actual build rules are +specified in as set of shared rules files in the $NACL_SDK_ROOT/tools +directory. + +This document describes some of the variables and macros used by in the +build system. For more details please see the .mk files in the tools +folder. + +Using the build system for new projects +-------------------------------------- + +It is perfectly possible to use the included build system for projects +outside of the Native Client SDK. A good starting point for doing this +would be to copy the Makefile from the hello_world example. In most +simple cases the only changes needed are to update the SOURCES and +TARGET variables. + +User Variables +-------------- + +TARGET + This variable holds the name of your project. Normally this is the + basename of the library or executable which is the final target. + +SOURCES + The list of sources to be built. In most cases this list is passed to + the compile and link macros. + +VALID_TOOLCHAINS + This variable can be used to control which toolchains are supported by + the project. Valid entries for this list are: newlib, glibc, pnacl, + linux, mac, win. The default value is: "newlib glibc pnacl". + +NACL_SDK_ROOT + Optionally force the build system to use a certain location for the + Native Client SDK. If not set within the Makefile the $NACL_SDK_ROOT + environment variable will by used. It is an error if this variable is + neither set within the Makefile nor in the environment. + +Macros / Rules +-------------- + +The following macros can be used in the Makefiles to generate the rules +for building the various kinds types of target. These are designed to +be used via the 'call' macro. e.g. $(call COMPILE_RULE,$(SOURCES)) + +COMPILE_RULE + This rule is used to build object files from a list of sources. + +SO_RULE + Used to build shared objects from a list of sources. + +LIB_RULE + Used to build static libraries from a list of sources. + +NMF_RULE + Used to build nmf metadata file from a native client executable (or + set of executables). This is needed in order to run the executable in + chrome. + +HTML_RULE + Used to build both html and nmf files from a native client executable + (or set of executables) which will allow the executable to be run + in chrome. + +For more information on how to use these rules in your Makefile see +the shared Makefiles in the tools folders: + +common.mk + Top level shared rules file that include the toolchain specific + rules. + +nacl_gcc.mk + Rules for building using the gcc-based NaCl toolchains. + +nacl_llvm.mk + Rules for building using the llvm-based PNaCl toolchains. + +host_gcc.mk + Rules for building using the linux/mac host gcc toolchain. + +host_vc.mk + Rules for building using the windows Visual Studio toolchain. + +.. vim: ft=rst tw=72 diff --git a/native_client_sdk/src/build_tools/build_projects.py b/native_client_sdk/src/build_tools/build_projects.py index db07e323b9..3b6780be6f 100755 --- a/native_client_sdk/src/build_tools/build_projects.py +++ b/native_client_sdk/src/build_tools/build_projects.py @@ -3,6 +3,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import multiprocessing import optparse import os import sys @@ -30,6 +31,12 @@ LIB_DICT = { } VALID_TOOLCHAINS = ['newlib', 'glibc', 'pnacl', 'win', 'linux', 'mac'] +# Global verbosity setting. +# If set to try (normally via a command line arg) then build_projects will +# add V=1 to all calls to 'make' +verbose = False + + def CopyFilesFromTo(filelist, srcdir, dstdir): for filename in filelist: @@ -38,9 +45,10 @@ def CopyFilesFromTo(filelist, srcdir, dstdir): buildbot_common.CopyFile(srcpath, dstpath) -def UpdateHelpers(pepperdir, platform, clobber=False): - if not os.path.exists(os.path.join(pepperdir, 'tools')): - buildbot_common.ErrorExit('Examples depend on missing tools.') +def UpdateHelpers(pepperdir, clobber=False): + tools_dir = os.path.join(pepperdir, 'tools') + if not os.path.exists(tools_dir): + buildbot_common.ErrorExit('SDK tools dir is missing: %s' % tools_dir) exampledir = os.path.join(pepperdir, 'examples') if clobber: @@ -58,15 +66,15 @@ def UpdateHelpers(pepperdir, platform, clobber=False): # Copy tools scripts and make includes buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.py'), - os.path.join(pepperdir, 'tools')) + tools_dir) buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.mk'), - os.path.join(pepperdir, 'tools')) + tools_dir) # On Windows add a prebuilt make - if platform == 'win': + if getos.GetPlatform() == 'win': buildbot_common.BuildStep('Add MAKE') http_download.HttpDownload(GSTORE + MAKE, - os.path.join(pepperdir, 'tools', 'make.exe')) + os.path.join(tools_dir, 'make.exe')) def ValidateToolchains(toolchains): @@ -76,7 +84,7 @@ def ValidateToolchains(toolchains): ', '.join(invalid_toolchains))) -def UpdateProjects(pepperdir, platform, project_tree, toolchains, +def UpdateProjects(pepperdir, project_tree, toolchains, clobber=False, configs=None, first_toolchain=False): if configs is None: configs = ['Debug', 'Release'] @@ -89,6 +97,7 @@ def UpdateProjects(pepperdir, platform, project_tree, toolchains, # Create the library output directories libdir = os.path.join(pepperdir, 'lib') + platform = getos.GetPlatform() for config in configs: for arch in LIB_DICT[platform]: dirpath = os.path.join(libdir, '%s_%s_host' % (platform, arch), config) @@ -141,44 +150,56 @@ def UpdateProjects(pepperdir, platform, project_tree, toolchains, targets) -def BuildProjectsBranch(pepperdir, platform, branch, deps, clean, config): +def BuildProjectsBranch(pepperdir, branch, deps, clean, config): make_dir = os.path.join(pepperdir, branch) print "\n\nMake: " + make_dir - if platform == 'win': + + if getos.GetPlatform() == 'win': # We need to modify the environment to build host on Windows. make = os.path.join(make_dir, 'make.bat') else: make = 'make' - extra_args = ['CONFIG='+config] + env = None + if os.environ.get('USE_GOMA') == '1': + env = dict(os.environ) + env['NACL_COMPILER_PREFIX'] = 'gomacc' + # Add -m32 to the CFLAGS when building using i686-nacl-gcc + # otherwise goma won't recognise it as different to the x86_64 + # build. + env['X86_32_CFLAGS'] = '-m32' + env['X86_32_CXXFLAGS'] = '-m32' + jobs = '50' + else: + jobs = str(multiprocessing.cpu_count()) + + make_cmd = [make, '-j', jobs, 'TOOLCHAIN=all'] + + make_cmd.append('CONFIG='+config) if not deps: - extra_args += ['IGNORE_DEPS=1'] + make_cmd.append('IGNORE_DEPS=1') - try: - buildbot_common.Run([make, '-j8', 'TOOLCHAIN=all'] + extra_args, - cwd=make_dir) - except: - print 'Failed to build ' + branch - raise + if verbose: + make_cmd.append('V=1') + buildbot_common.Run(make_cmd, cwd=make_dir, env=env) if clean: # Clean to remove temporary files but keep the built - buildbot_common.Run([make, '-j8', 'clean', 'TOOLCHAIN=all'] + extra_args, - cwd=make_dir) + buildbot_common.Run(make_cmd + ['clean'], cwd=make_dir, env=env) -def BuildProjects(pepperdir, platform, project_tree, deps=True, +def BuildProjects(pepperdir, project_tree, deps=True, clean=False, config='Debug'): # First build libraries build_order = ['src', 'testlibs'] for branch in build_order: if branch in project_tree: - BuildProjectsBranch(pepperdir, platform, branch, deps, clean, config) + BuildProjectsBranch(pepperdir, branch, deps, clean, config) # Build everything else. for branch in project_tree: if branch not in build_order: - BuildProjectsBranch(pepperdir, platform, branch, deps, clean, config) + BuildProjectsBranch(pepperdir, branch, deps, clean, config) def main(args): @@ -215,15 +236,14 @@ def main(args): pepper_ver = str(int(build_version.ChromeMajorVersion())) pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver) - platform = getos.GetPlatform() if not options.toolchain: options.toolchain = ['newlib', 'glibc', 'pnacl', 'host'] if 'host' in options.toolchain: options.toolchain.remove('host') - options.toolchain.append(platform) - print 'Adding platform: ' + platform + options.toolchain.append(getos.GetPlatform()) + print 'Adding platform: ' + getos.GetPlatform() ValidateToolchains(options.toolchain) @@ -243,17 +263,21 @@ def main(args): project_tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, filters=filters) parse_dsc.PrintProjectTree(project_tree) - UpdateHelpers(pepperdir, platform, clobber=options.clobber) - UpdateProjects(pepperdir, platform, project_tree, options.toolchain, + UpdateHelpers(pepperdir, clobber=options.clobber) + UpdateProjects(pepperdir, project_tree, options.toolchain, clobber=options.clobber) + if options.verbose: + global verbose + verbose = True + if options.build: if options.config: configs = [options.config] else: configs = ['Debug', 'Release'] for config in configs: - BuildProjects(pepperdir, platform, project_tree, config=config) + BuildProjects(pepperdir, project_tree, config=config) return 0 diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py index 7b189912ce..0212d0741f 100755 --- a/native_client_sdk/src/build_tools/build_sdk.py +++ b/native_client_sdk/src/build_tools/build_sdk.py @@ -42,7 +42,7 @@ import parse_dsc import verify_filelist from build_paths import SCRIPT_DIR, SDK_SRC_DIR, SRC_DIR, NACL_DIR, OUT_DIR -from build_paths import PPAPI_DIR, NACLPORTS_DIR, GSTORE +from build_paths import NACLPORTS_DIR, GSTORE # Add SDK make tools scripts to the python path. sys.path.append(os.path.join(SDK_SRC_DIR, 'tools')) @@ -61,21 +61,21 @@ GYPBUILD_DIR = 'gypbuild' options = None -def GetGlibcToolchain(platform, arch): +def GetGlibcToolchain(arch): tcdir = os.path.join(NACL_DIR, 'toolchain', '.tars') - tcname = 'toolchain_%s_%s.tar.bz2' % (platform, arch) + tcname = 'toolchain_%s_%s.tar.bz2' % (getos.GetPlatform(), arch) return os.path.join(tcdir, tcname) -def GetNewlibToolchain(platform, arch): +def GetNewlibToolchain(arch): tcdir = os.path.join(NACL_DIR, 'toolchain', '.tars') - tcname = 'naclsdk_%s_%s.tgz' % (platform, arch) + tcname = 'naclsdk_%s_%s.tgz' % (getos.GetPlatform(), arch) return os.path.join(tcdir, tcname) -def GetPNaClToolchain(os_platform, arch): +def GetPNaClToolchain(arch): tcdir = os.path.join(NACL_DIR, 'toolchain', '.tars') - tcname = 'naclsdk_pnacl_%s_%s.tgz' % (os_platform, arch) + tcname = 'naclsdk_pnacl_%s_%s.tgz' % (getos.GetPlatform(), arch) return os.path.join(tcdir, tcname) @@ -120,7 +120,7 @@ def GetSconsArgs(tcpath, outdir, arch, xarch=None): Only used for pnacl builds. """ - if sys.platform in ['cygwin', 'win32']: + if getos.GetPlatform() == 'win': scons = 'scons.bat' else: scons = './scons' @@ -154,17 +154,23 @@ def BuildStepMakePepperDirs(pepperdir, subdirs): for subdir in subdirs: buildbot_common.MakeDir(os.path.join(pepperdir, subdir)) +TEXT_FILES = [ + 'AUTHORS', + 'COPYING', + 'LICENSE', + 'README.Makefiles', +] -def BuildStepCopyTextFiles(pepperdir, pepper_ver, revision): +def BuildStepCopyTextFiles(pepperdir, pepper_ver, chrome_revision, + nacl_revision): buildbot_common.BuildStep('Add Text Files') - files = ['AUTHORS', 'COPYING', 'LICENSE'] - files = [os.path.join(SDK_SRC_DIR, filename) for filename in files] - oshelpers.Copy(['-v'] + files + [pepperdir]) + InstallFiles(SDK_SRC_DIR, pepperdir, TEXT_FILES) # Replace a few placeholders in README readme_text = open(os.path.join(SDK_SRC_DIR, 'README')).read() readme_text = readme_text.replace('${VERSION}', pepper_ver) - readme_text = readme_text.replace('${REVISION}', revision) + readme_text = readme_text.replace('${CHROME_REVISION}', chrome_revision) + readme_text = readme_text.replace('${NACL_REVISION}', nacl_revision) # Year/Month/Day Hour:Minute:Second time_format = '%Y/%m/%d %H:%M:%S' @@ -174,8 +180,9 @@ def BuildStepCopyTextFiles(pepperdir, pepper_ver, revision): open(os.path.join(pepperdir, 'README'), 'w').write(readme_text) -def BuildStepUntarToolchains(pepperdir, platform, arch, toolchains): +def BuildStepUntarToolchains(pepperdir, arch, toolchains): buildbot_common.BuildStep('Untar Toolchains') + platform = getos.GetPlatform() tcname = platform + '_' + arch tmpdir = os.path.join(OUT_DIR, 'tc_temp') buildbot_common.RemoveDir(tmpdir) @@ -183,7 +190,7 @@ def BuildStepUntarToolchains(pepperdir, platform, arch, toolchains): if 'newlib' in toolchains: # Untar the newlib toolchains - tarfile = GetNewlibToolchain(platform, arch) + tarfile = GetNewlibToolchain(arch) buildbot_common.Run([sys.executable, CYGTAR, '-C', tmpdir, '-xf', tarfile], cwd=NACL_DIR) @@ -202,7 +209,7 @@ def BuildStepUntarToolchains(pepperdir, platform, arch, toolchains): if 'glibc' in toolchains: # Untar the glibc toolchains - tarfile = GetGlibcToolchain(platform, arch) + tarfile = GetGlibcToolchain(arch) buildbot_common.Run([sys.executable, CYGTAR, '-C', tmpdir, '-xf', tarfile], cwd=NACL_DIR) @@ -216,7 +223,7 @@ def BuildStepUntarToolchains(pepperdir, platform, arch, toolchains): tmpdir = os.path.join(tmpdir, 'pnacl') buildbot_common.RemoveDir(tmpdir) buildbot_common.MakeDir(tmpdir) - tarfile = GetPNaClToolchain(platform, arch) + tarfile = GetPNaClToolchain(arch) buildbot_common.Run([sys.executable, CYGTAR, '-C', tmpdir, '-xf', tarfile], cwd=NACL_DIR) @@ -226,7 +233,7 @@ def BuildStepUntarToolchains(pepperdir, platform, arch, toolchains): buildbot_common.RemoveDir(tmpdir) - if options.gyp and sys.platform not in ['cygwin', 'win32']: + if options.gyp and platform != 'win': # If the gyp options is specified we install a toolchain # wrapper so that gyp can switch toolchains via a commandline # option. @@ -274,79 +281,6 @@ NACL_HEADER_MAP = { 'host': [] } -# Source relative to 'ppapi' foler. Destiniation relative -# to SDK include folder. -PPAPI_HEADER_MAP = [ - # Copy the KHR headers - ('lib/gl/include/KHR/khrplatform.h', 'KHR/'), - - # Copy the GLES2 headers - ('lib/gl/include/GLES2/gl2.h', 'GLES2/'), - ('lib/gl/include/GLES2/gl2ext.h', 'GLES2/'), - ('lib/gl/include/GLES2/gl2platform.h', 'GLES2/'), - - # Copy the EGL headers - ('lib/gl/include/EGL/egl.h', 'EGL/'), - ('lib/gl/include/EGL/eglext.h', 'EGL/'), - ('lib/gl/include/EGL/eglplatform.h', 'EGL/'), - - # Copy in the gles2 headers - ('lib/gl/gles2/gl2ext_ppapi.h', 'ppapi/gles2/'), - # Create a duplicate copy of this header - # TODO(sbc), remove this copy once we find a way to build gl2ext_ppapi.c. - ('lib/gl/gles2/gl2ext_ppapi.h', 'ppapi/lib/gl/gles2/'), - - # Copy in the C++ headers - ('utility/graphics/paint_aggregator.h', 'ppapi/utility/graphics/'), - ('utility/graphics/paint_manager.h', 'ppapi/utility/graphics/'), - ('utility/threading/lock.h', 'ppapi/utility/threading/'), - ('utility/threading/simple_thread.h', 'ppapi/utility/threading/'), - ('utility/websocket/websocket_api.h', 'ppapi/utility/websocket/'), - ('utility/completion_callback_factory.h','ppapi/utility/'), - ('utility/completion_callback_factory_thread_traits.h', 'ppapi/utility/'), - - # Copy in c, c/dev and c/extensions/dev headers - # TODO(sbc): remove the use of wildcards here so that we can more - # tightly control what ends up in the SDK. - ('c/*.h', 'ppapi/c/'), - ('c/dev/*.h', 'ppapi/c/dev/'), - ('c/extensions/dev/*.h', 'ppapi/c/extensions/dev/'), - - # Copy in cpp, cpp/dev, cpp/extensions/, cpp/extensions/dev - ('cpp/*.h', 'ppapi/cpp/'), - ('cpp/extensions/*.h', 'ppapi/cpp/extensions/'), - ('cpp/dev/*.h', 'ppapi/cpp/dev/'), - ('cpp/extensions/dev/*.h', 'ppapi/cpp/extensions/dev/'), - - # Copy certain private headers (specifically these are the ones - # that are used by nacl-mounts) - ('cpp/private/ext_crx_file_system_private.h', 'ppapi/cpp/private/'), - ('cpp/private/file_io_private.h', 'ppapi/cpp/private/'), - ('cpp/private/net_address_private.h', 'ppapi/cpp/private/'), - ('cpp/private/tcp_server_socket_private.h', 'ppapi/cpp/private/'), - ('cpp/private/host_resolver_private.h', 'ppapi/cpp/private/'), - ('cpp/private/pass_file_handle.h', 'ppapi/cpp/private/'), - ('cpp/private/tcp_socket_private.h', 'ppapi/cpp/private/'), - ('cpp/private/udp_socket_private.h', 'ppapi/cpp/private/'), - ('cpp/private/x509_certificate_private.h', 'ppapi/cpp/private/'), - - ('c/private/pp_file_handle.h', 'ppapi/c/private/'), - ('c/private/ppb_ext_crx_file_system_private.h', 'ppapi/c/private/'), - ('c/private/ppb_file_io_private.h', 'ppapi/c/private/'), - ('c/private/ppb_file_ref_private.h', 'ppapi/c/private/'), - ('c/private/ppb_host_resolver_private.h', 'ppapi/c/private/'), - ('c/private/ppb_tcp_server_socket_private.h', 'ppapi/c/private/'), - ('c/private/ppb_net_address_private.h', 'ppapi/c/private/'), - ('c/private/ppb_tcp_socket_private.h', 'ppapi/c/private/'), - ('c/private/ppb_udp_socket_private.h', 'ppapi/c/private/'), - ('c/private/ppb_x509_certificate_private.h', 'ppapi/c/private/'), -] - - -def InstallCommonHeaders(inc_path): - InstallFiles(PPAPI_DIR, inc_path, PPAPI_HEADER_MAP) - - def InstallFiles(src_root, dest_root, file_list): """Copy a set of files from src_root to dest_root according to the given mapping. This allows files to be copied from @@ -435,7 +369,7 @@ TOOLCHAIN_LIBS = { } -def GypNinjaInstall(pepperdir, platform, toolchains): +def GypNinjaInstall(pepperdir, toolchains): build_dir = GYPBUILD_DIR ninja_out_dir = os.path.join(OUT_DIR, build_dir, 'Release') tools_files = [ @@ -444,18 +378,17 @@ def GypNinjaInstall(pepperdir, platform, toolchains): ['irt_core_newlib_x32.nexe', 'irt_core_x86_32.nexe'], ['irt_core_newlib_x64.nexe', 'irt_core_x86_64.nexe'], ] - if sys.platform not in ['cygwin', 'win32']: - minidump_files = [ - ['dump_syms', 'dump_syms'], - ['minidump_dump', 'minidump_dump'], - ['minidump_stackwalk', 'minidump_stackwalk'] - ] - tools_files.extend(minidump_files) + + platform = getos.GetPlatform() # TODO(binji): dump_syms doesn't currently build on Windows. See # http://crbug.com/245456 if platform != 'win': - tools_files.append(['dump_syms', 'dump_syms']) + tools_files += [ + ['dump_syms', 'dump_syms'], + ['minidump_dump', 'minidump_dump'], + ['minidump_stackwalk', 'minidump_stackwalk'] + ] if platform != 'mac': # Mac doesn't build 64-bit binaries. @@ -504,7 +437,7 @@ def GypNinjaInstall(pepperdir, platform, toolchains): -def GypNinjaBuild_NaCl(platform, rel_out_dir): +def GypNinjaBuild_NaCl(rel_out_dir): gyp_py = os.path.join(NACL_DIR, 'build', 'gyp_nacl') nacl_core_sdk_gyp = os.path.join(NACL_DIR, 'build', 'nacl_core_sdk.gyp') all_gyp = os.path.join(NACL_DIR, 'build', 'all.gyp') @@ -515,6 +448,7 @@ def GypNinjaBuild_NaCl(platform, rel_out_dir): GypNinjaBuild('arm', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_arm) GypNinjaBuild('ia32', gyp_py, all_gyp, 'ncval_new', out_dir) + platform = getos.GetPlatform() if platform == 'win': NinjaBuild('sel_ldr64', out_dir) elif platform == 'linux': @@ -534,10 +468,10 @@ def GypNinjaBuild_NaCl(platform, rel_out_dir): os.path.join(SRC_DIR, out_dir, 'Release', dst)) -def GypNinjaBuild_Breakpad(platform, rel_out_dir): +def GypNinjaBuild_Breakpad(rel_out_dir): # TODO(binji): dump_syms doesn't currently build on Windows. See # http://crbug.com/245456 - if platform == 'win': + if getos.GetPlatform() == 'win': return gyp_py = os.path.join(SRC_DIR, 'build', 'gyp_chromium') @@ -613,12 +547,13 @@ def NinjaBuild(targets, out_dir): buildbot_common.Run(['ninja', '-C', out_config_dir] + targets, cwd=SRC_DIR) -def BuildStepBuildToolchains(pepperdir, platform, toolchains): +def BuildStepBuildToolchains(pepperdir, toolchains): buildbot_common.BuildStep('SDK Items') - GypNinjaBuild_NaCl(platform, GYPBUILD_DIR) - GypNinjaBuild_Breakpad(platform, GYPBUILD_DIR) + GypNinjaBuild_NaCl(GYPBUILD_DIR) + GypNinjaBuild_Breakpad(GYPBUILD_DIR) + platform = getos.GetPlatform() tcname = platform + '_x86' newlibdir = os.path.join(pepperdir, 'toolchain', tcname + '_newlib') glibcdir = os.path.join(pepperdir, 'toolchain', tcname + '_glibc') @@ -630,7 +565,7 @@ def BuildStepBuildToolchains(pepperdir, platform, toolchains): if 'arm' in toolchains: GypNinjaBuild_PPAPI('arm', GYPBUILD_DIR + '-arm') - GypNinjaInstall(pepperdir, platform, toolchains) + GypNinjaInstall(pepperdir, toolchains) if 'newlib' in toolchains: InstallNaClHeaders(GetToolchainNaClInclude('newlib', newlibdir, 'x86'), @@ -693,12 +628,12 @@ def MakeDirectoryOrClobber(pepperdir, dirname, clobber): return dirpath -def BuildStepUpdateHelpers(pepperdir, platform, clobber): +def BuildStepUpdateHelpers(pepperdir, clobber): buildbot_common.BuildStep('Update project helpers') - build_projects.UpdateHelpers(pepperdir, platform, clobber=clobber) + build_projects.UpdateHelpers(pepperdir, clobber=clobber) -def BuildStepUpdateUserProjects(pepperdir, platform, toolchains, +def BuildStepUpdateUserProjects(pepperdir, toolchains, build_experimental, clobber): buildbot_common.BuildStep('Update examples and libraries') @@ -714,7 +649,7 @@ def BuildStepUpdateUserProjects(pepperdir, platform, toolchains, if 'host' in toolchains: toolchains.remove('host') - toolchains.append(platform) + toolchains.append(getos.GetPlatform()) filters['TOOLS'] = toolchains @@ -728,37 +663,20 @@ def BuildStepUpdateUserProjects(pepperdir, platform, toolchains, ] tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, filters=filters) - build_projects.UpdateProjects(pepperdir, platform, tree, clobber=clobber, + build_projects.UpdateProjects(pepperdir, tree, clobber=clobber, toolchains=toolchains) -def BuildStepMakeAll(pepperdir, platform, directory, step_name, - clean=False, deps=True, config='Debug'): +def BuildStepMakeAll(pepperdir, directory, step_name, + deps=True, clean=False, config='Debug'): buildbot_common.BuildStep(step_name) - make_dir = os.path.join(pepperdir, directory) + build_projects.BuildProjectsBranch(pepperdir, directory, clean, deps, config) - print "\n\nMake: " + make_dir - if platform == 'win': - make = os.path.join(make_dir, 'make.bat') - else: - make = 'make' - - extra_args = ['CONFIG='+config] - if not deps: - extra_args += ['IGNORE_DEPS=1'] - - buildbot_common.Run([make, '-j8', 'TOOLCHAIN=all'] + extra_args, - cwd=make_dir) - if clean: - # Clean to remove temporary files but keep the built libraries. - buildbot_common.Run([make, '-j8', 'clean', 'TOOLCHAIN=all'] + extra_args, - cwd=make_dir) - -def BuildStepBuildLibraries(pepperdir, platform, directory): - BuildStepMakeAll(pepperdir, platform, directory, 'Build Libraries Debug', +def BuildStepBuildLibraries(pepperdir, directory): + BuildStepMakeAll(pepperdir, directory, 'Build Libraries Debug', clean=True, config='Debug') - BuildStepMakeAll(pepperdir, platform, directory, 'Build Libraries Release', + BuildStepMakeAll(pepperdir, directory, 'Build Libraries Release', clean=True, config='Release') @@ -782,11 +700,11 @@ def GenerateNotice(fileroot, output_filename='NOTICE', extra_files=None): generate_notice.Generate(output_filename, fileroot, license_files) -def BuildStepVerifyFilelist(pepperdir, platform): +def BuildStepVerifyFilelist(pepperdir): buildbot_common.BuildStep('Verify SDK Files') file_list_path = os.path.join(SCRIPT_DIR, 'sdk_files.list') try: - verify_filelist.Verify(platform, file_list_path, pepperdir) + verify_filelist.Verify(file_list_path, pepperdir) print 'OK' except verify_filelist.ParseException, e: buildbot_common.ErrorExit('Parsing sdk_files.list failed:\n\n%s' % e) @@ -818,7 +736,8 @@ def BuildStepTarBundle(pepper_ver, tarfile): -def GetManifestBundle(pepper_ver, revision, tarfile, archive_url): +def GetManifestBundle(pepper_ver, chrome_revision, nacl_revision, tarfile, + archive_url): with open(tarfile, 'rb') as tarfile_stream: archive_sha1, archive_size = manifest_util.DownloadAndComputeHash( tarfile_stream) @@ -829,17 +748,20 @@ def GetManifestBundle(pepper_ver, revision, tarfile, archive_url): archive.checksum = archive_sha1 bundle = manifest_util.Bundle('pepper_' + pepper_ver) - bundle.revision = int(revision) + bundle.revision = int(chrome_revision) bundle.repath = 'pepper_' + pepper_ver bundle.version = int(pepper_ver) - bundle.description = 'Chrome %s bundle, revision %s' % (pepper_ver, revision) + bundle.description = ( + 'Chrome %s bundle. Chrome revision: %s. NaCl revision: %s' % ( + pepper_ver, chrome_revision, nacl_revision)) bundle.stability = 'dev' bundle.recommended = 'no' bundle.archives = [archive] return bundle -def BuildStepArchiveBundle(name, pepper_ver, revision, tarfile): +def BuildStepArchiveBundle(name, pepper_ver, chrome_revision, nacl_revision, + tarfile): buildbot_common.BuildStep('Archive %s' % name) bucket_path = 'nativeclient-mirror/nacl/nacl_sdk/%s' % ( build_version.ChromeVersion(),) @@ -850,7 +772,8 @@ def BuildStepArchiveBundle(name, pepper_ver, revision, tarfile): # generate "manifest snippet" for this archive. archive_url = GSTORE + 'nacl_sdk/%s/%s' % ( build_version.ChromeVersion(), tarname) - bundle = GetManifestBundle(pepper_ver, revision, tarfile, archive_url) + bundle = GetManifestBundle(pepper_ver, chrome_revision, nacl_revision, + tarfile, archive_url) manifest_snippet_file = os.path.join(OUT_DIR, tarname + '.json') with open(manifest_snippet_file, 'wb') as manifest_snippet_stream: @@ -956,7 +879,6 @@ def main(args): global options options, args = parser.parse_args(args[1:]) - platform = getos.GetPlatform() arch = 'x86' generate_make.use_gyp = options.gyp @@ -971,49 +893,48 @@ def main(args): parser.error('Incompatible arguments with archive.') chrome_version = int(build_version.ChromeMajorVersion()) - clnumber = build_version.ChromeRevision() + chrome_revision = build_version.ChromeRevision() + nacl_revision = build_version.NaClRevision() pepper_ver = str(chrome_version) pepper_old = str(chrome_version - 1) pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver) pepperdir_old = os.path.join(OUT_DIR, 'pepper_' + pepper_old) - tarname = 'naclsdk_' + platform + '.tar.bz2' + tarname = 'naclsdk_' + getos.GetPlatform() + '.tar.bz2' tarfile = os.path.join(OUT_DIR, tarname) if options.release: pepper_ver = options.release - print 'Building PEPPER %s at %s' % (pepper_ver, clnumber) + print 'Building PEPPER %s at %s' % (pepper_ver, chrome_revision) if 'NACL_SDK_ROOT' in os.environ: # We don't want the currently configured NACL_SDK_ROOT to have any effect # of the build. del os.environ['NACL_SDK_ROOT'] - BuildStepCleanPepperDirs(pepperdir, pepperdir_old) - BuildStepMakePepperDirs(pepperdir, ['include', 'toolchain', 'tools']) - if not options.skip_toolchain: + BuildStepCleanPepperDirs(pepperdir, pepperdir_old) + BuildStepMakePepperDirs(pepperdir, ['include', 'toolchain', 'tools']) BuildStepDownloadToolchains() - BuildStepUntarToolchains(pepperdir, platform, arch, toolchains) + BuildStepUntarToolchains(pepperdir, arch, toolchains) - BuildStepCopyTextFiles(pepperdir, pepper_ver, clnumber) - BuildStepBuildToolchains(pepperdir, platform, toolchains) - InstallCommonHeaders(os.path.join(pepperdir, 'include')) + BuildStepCopyTextFiles(pepperdir, pepper_ver, chrome_revision, nacl_revision) + BuildStepBuildToolchains(pepperdir, toolchains) - BuildStepUpdateHelpers(pepperdir, platform, True) - BuildStepUpdateUserProjects(pepperdir, platform, toolchains, + BuildStepUpdateHelpers(pepperdir, True) + BuildStepUpdateUserProjects(pepperdir, toolchains, options.build_experimental, True) # Ship with libraries prebuilt, so run that first. - BuildStepBuildLibraries(pepperdir, platform, 'src') + BuildStepBuildLibraries(pepperdir, 'src') GenerateNotice(pepperdir) # Verify the SDK contains what we expect. - BuildStepVerifyFilelist(pepperdir, platform) + BuildStepVerifyFilelist(pepperdir) if not options.skip_tar: BuildStepTarBundle(pepper_ver, tarfile) - if options.build_ports and platform == 'linux': + if options.build_ports and getos.GetPlatform() == 'linux': ports_tarfile = os.path.join(OUT_DIR, 'naclports.tar.bz2') BuildStepSyncNaClPorts() BuildStepBuildNaClPorts(pepper_ver, pepperdir) @@ -1022,9 +943,11 @@ def main(args): # Archive on non-trybots. if options.archive: - BuildStepArchiveBundle('build', pepper_ver, clnumber, tarfile) - if options.build_ports and platform == 'linux': - BuildStepArchiveBundle('naclports', pepper_ver, clnumber, ports_tarfile) + BuildStepArchiveBundle('build', pepper_ver, chrome_revision, nacl_revision, + tarfile) + if options.build_ports and getos.GetPlatform() == 'linux': + BuildStepArchiveBundle('naclports', pepper_ver, chrome_revision, + nacl_revision, ports_tarfile) BuildStepArchiveSDKTools() return 0 diff --git a/native_client_sdk/src/build_tools/build_version.py b/native_client_sdk/src/build_tools/build_version.py index ffe8cf0d38..6e0aaa7421 100644 --- a/native_client_sdk/src/build_tools/build_version.py +++ b/native_client_sdk/src/build_tools/build_version.py @@ -62,3 +62,12 @@ def ChromeRevision(): The Chrome revision as a string. e.g. "12345" ''' return lastchange.FetchVersionInfo(None).revision + +def NaClRevision(): + '''Extract NaCl revision from svn. + + Returns: + The NaCl revision as a string. e.g. "12345" + ''' + nacl_dir = os.path.join(SRC_DIR, 'native_client') + return lastchange.FetchVersionInfo(None, nacl_dir).revision diff --git a/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json b/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json index 3c237f5b6d..9d415c9a02 100644 --- a/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json +++ b/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json @@ -35,16 +35,6 @@ }, { "archives": [], - "description": "Chrome 23 bundle, revision xxxxx", - "name": "pepper_23", - "recommended": "no", - "repath": "pepper_23", - "revision": 0, - "stability": "post_stable", - "version": 23 - }, - { - "archives": [], "description": "Chrome 24 bundle, revision xxxxx", "name": "pepper_24", "recommended": "no", @@ -67,20 +57,20 @@ "archives": [], "description": "Chrome 26 bundle, revision xxxxx", "name": "pepper_26", - "recommended": "yes", + "recommended": "no", "repath": "pepper_26", "revision": 0, - "stability": "stable", + "stability": "post_stable", "version": 26 }, { "archives": [], "description": "Chrome 27 bundle, revision xxxxx", "name": "pepper_27", - "recommended": "no", + "recommended": "yes", "repath": "pepper_27", "revision": 0, - "stability": "beta", + "stability": "stable", "version": 27 }, { @@ -90,11 +80,21 @@ "recommended": "no", "repath": "pepper_28", "revision": 0, - "stability": "dev", + "stability": "beta", "version": 28 }, { "archives": [], + "description": "Chrome 29 bundle, revision xxxxx", + "name": "pepper_29", + "recommended": "no", + "repath": "pepper_29", + "revision": 0, + "stability": "dev", + "version": 29 + }, + { + "archives": [], "description": "Chrome Canary", "name": "pepper_canary", "recommended": "no", diff --git a/native_client_sdk/src/build_tools/library.mk b/native_client_sdk/src/build_tools/library.mk index 2ee3490006..1d44b3316b 100644 --- a/native_client_sdk/src/build_tools/library.mk +++ b/native_client_sdk/src/build_tools/library.mk @@ -1,78 +1,37 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Copyright (c) 2013 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. - -# -# GNU Make based build file. For details on GNU Make see: -# http://www.gnu.org/software/make/manual/make.html -# -# - -# Default configuration -# -# By default we will build a Debug configuration using the GCC newlib toolchain -# to override this, specify TOOLCHAIN=newlib|glibc or CONFIG=Debug|Release on -# the make command-line or in this file prior to including common.mk. The -# toolchain we use by default will be the first valid one listed -VALID_TOOLCHAINS:={{' '.join(tools)}} - - -[[# Only one target is allowed in a library project]] [[target = targets[0] ]] -[[name = target['NAME'] ]] [[flags = ' '.join(target.get('CCFLAGS', []))]] [[flags += ' '.join(target.get('CXXFLAGS', []))]] -# -# Get pepper directory for toolchain and includes. -# -# If NACL_SDK_ROOT is not set, then assume it can be found relative to -# to this Makefile. -# -NACL_SDK_ROOT?=$(abspath $(CURDIR)/../..) -EXTRA_INC_PATHS={{' '.join(target.get('INCLUDES', []))}} +# GNU Makefile based on shared rules provided by the Native Client SDK. +# See README.Makefiles for more details. -include $(NACL_SDK_ROOT)/tools/common.mk +VALID_TOOLCHAINS := {{' '.join(tools)}} +NACL_SDK_ROOT ?= $(abspath $(CURDIR)/../..) +[[if 'INCLUDES' in target:]] +EXTRA_INC_PATHS={{' '.join(target['INCLUDES'])}} +[[]] -# -# Target Name -# -# The base name of the final library, also the name of the NMF file containing -# the mapping between architecture and actual NEXE. -# -TARGET={{name}} +include $(NACL_SDK_ROOT)/tools/common.mk -# -# List of sources to compile -# -SOURCES= \ +TARGET = {{target['NAME']}} +CFLAGS = {{flags}} +SOURCES = \ [[for source in sorted(target['SOURCES']):]] {{source}} \ [[]] +all: install - -# -# Use the compile macro for each source. +# Build rules generated by macros from common.mk: # -$(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),{{flags}}))) - -# -# Use the lib macro for this target on the list of sources. -# -$(eval $(call LIB_RULE,{{name}},$(SOURCES))) +$(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS)))) +$(eval $(call LIB_RULE,$(TARGET),$(SOURCES))) [[if target['TYPE'] != 'static-lib':]] ifeq ($(TOOLCHAIN),glibc) -# -# When building with GLIBC, also build a shared object version of the -# library. -# -$(eval $(call SO_RULE,{{name}},$(SOURCES))) +$(eval $(call SO_RULE,$(TARGET),$(SOURCES))) endif [[]] - -# -# Install the resulting libraries in the SDK library directory. -# -all: install diff --git a/native_client_sdk/src/build_tools/parse_dsc.py b/native_client_sdk/src/build_tools/parse_dsc.py index fb04af8951..f26b908ef4 100755 --- a/native_client_sdk/src/build_tools/parse_dsc.py +++ b/native_client_sdk/src/build_tools/parse_dsc.py @@ -9,12 +9,13 @@ import optparse import os import sys +VALID_TOOLCHAINS = ['newlib', 'glibc', 'pnacl', 'win', 'linux', 'mac'] + # 'KEY' : ( <TYPE>, [Accepted Values], <Required?>) DSC_FORMAT = { 'DISABLE': (bool, [True, False], False), 'DISABLE_PACKAGE': (bool, [True, False], False), - 'TOOLS' : (list, ['newlib:arm', 'newlib:x64', 'newlib:x86', 'newlib', - 'glibc', 'pnacl', 'win', 'linux'], True), + 'TOOLS' : (list, VALID_TOOLCHAINS, True), 'CONFIGS' : (list, ['Debug', 'Release'], False), 'PREREQ' : (list, '', False), 'TARGETS' : (list, { diff --git a/native_client_sdk/src/build_tools/sdk_files.list b/native_client_sdk/src/build_tools/sdk_files.list index 97d7cb8716..ffc959ac98 100644 --- a/native_client_sdk/src/build_tools/sdk_files.list +++ b/native_client_sdk/src/build_tools/sdk_files.list @@ -114,6 +114,31 @@ examples/demo/drive/Makefile examples/demo/drive/manifest.json [win]examples/demo/make.bat examples/demo/Makefile +examples/demo/earth/background.js +examples/demo/earth/common.js +examples/demo/earth/example.js +examples/demo/earth/icon128.png +examples/demo/earth/index.html +examples/demo/earth/Makefile +[win]examples/demo/earth/make.bat +examples/demo/earth/manifest.json +examples/demo/earth/earth.cc +examples/demo/earth/earth.jpg +examples/demo/earth/earthnight.jpg +[win]examples/demo/flock/make.bat +examples/demo/flock/Makefile +examples/demo/flock/background.js +examples/demo/flock/common.js +examples/demo/flock/flock.cc +examples/demo/flock/goose.cc +examples/demo/flock/goose.h +examples/demo/flock/icon128.png +examples/demo/flock/images/flock_green.raw +examples/demo/flock/index.html +examples/demo/flock/manifest.json +examples/demo/flock/sprite.cc +examples/demo/flock/sprite.h +examples/demo/flock/vector2.h [win]examples/demo/life/make.bat examples/demo/life/Makefile examples/demo/life/background.js @@ -214,9 +239,6 @@ examples/tutorial/load_progress/Makefile examples/tutorial/load_progress/manifest.json [win]examples/tutorial/make.bat examples/tutorial/Makefile -include/EGL/eglext.h -include/EGL/egl.h -include/EGL/eglplatform.h include/error_handling/error_handling.h include/error_handling/string_stream.h include/GLES2/gl2ext.h @@ -234,6 +256,7 @@ include/json/writer.h include/KHR/khrplatform.h include/nacl_io/error.h include/nacl_io/inode_pool.h +include/nacl_io/ioctl.h include/nacl_io/kernel_handle.h include/nacl_io/kernel_intercept.h include/nacl_io/kernel_object.h @@ -242,6 +265,7 @@ include/nacl_io/kernel_wrap.h include/nacl_io/kernel_wrap_real.h include/nacl_io/mount_dev.h include/nacl_io/mount.h +include/nacl_io/mount_factory.h include/nacl_io/mount_html5fs.h include/nacl_io/mount_http.h include/nacl_io/mount_mem.h @@ -257,12 +281,15 @@ include/nacl_io/osinttypes.h include/nacl_io/osmman.h include/nacl_io/osstat.h include/nacl_io/ostypes.h +include/nacl_io/osunistd.h +include/nacl_io/osutime.h include/nacl_io/path.h include/nacl_io/pepper/all_interfaces.h include/nacl_io/pepper/define_empty_macros.h include/nacl_io/pepper_interface.h include/nacl_io/pepper/undef_macros.h include/nacl_io/real_pepper_interface.h +include/nacl_io/typed_mount_factory.h include/ppapi/c/dev/deprecated_bool.h include/ppapi/c/dev/ppb_audio_input_dev.h include/ppapi/c/dev/ppb_buffer_dev.h @@ -275,25 +302,19 @@ include/ppapi/c/dev/ppb_find_dev.h include/ppapi/c/dev/ppb_font_dev.h include/ppapi/c/dev/ppb_gles_chromium_texture_mapping_dev.h include/ppapi/c/dev/ppb_graphics_2d_dev.h -include/ppapi/c/dev/ppb_host_resolver_dev.h include/ppapi/c/dev/ppb_ime_input_event_dev.h include/ppapi/c/dev/ppb_keyboard_input_event_dev.h include/ppapi/c/dev/ppb_memory_dev.h -include/ppapi/c/dev/ppb_net_address_dev.h include/ppapi/c/dev/ppb_opengles2ext_dev.h include/ppapi/c/dev/ppb_printing_dev.h include/ppapi/c/dev/ppb_resource_array_dev.h include/ppapi/c/dev/ppb_scrollbar_dev.h -include/ppapi/c/dev/ppb_tcp_socket_dev.h include/ppapi/c/dev/ppb_testing_dev.h include/ppapi/c/dev/ppb_text_input_dev.h include/ppapi/c/dev/ppb_trace_event_dev.h include/ppapi/c/dev/ppb_truetype_font_dev.h -include/ppapi/c/dev/ppb_udp_socket_dev.h include/ppapi/c/dev/ppb_url_util_dev.h -include/ppapi/c/dev/ppb_var_array_dev.h include/ppapi/c/dev/ppb_var_deprecated.h -include/ppapi/c/dev/ppb_var_dictionary_dev.h include/ppapi/c/dev/ppb_video_capture_dev.h include/ppapi/c/dev/ppb_video_decoder_dev.h include/ppapi/c/dev/ppb_view_dev.h @@ -333,6 +354,7 @@ include/ppapi/c/ppb_gamepad.h include/ppapi/c/ppb_graphics_2d.h include/ppapi/c/ppb_graphics_3d.h include/ppapi/c/ppb.h +include/ppapi/c/ppb_host_resolver.h include/ppapi/c/ppb_image_data.h include/ppapi/c/ppb_input_event.h include/ppapi/c/ppb_instance.h @@ -340,12 +362,18 @@ include/ppapi/c/ppb_message_loop.h include/ppapi/c/ppb_messaging.h include/ppapi/c/ppb_mouse_cursor.h include/ppapi/c/ppb_mouse_lock.h +include/ppapi/c/ppb_net_address.h +include/ppapi/c/ppb_network_proxy.h include/ppapi/c/pp_bool.h include/ppapi/c/ppb_opengles2.h +include/ppapi/c/ppb_tcp_socket.h +include/ppapi/c/ppb_udp_socket.h include/ppapi/c/ppb_url_loader.h include/ppapi/c/ppb_url_request_info.h include/ppapi/c/ppb_url_response_info.h include/ppapi/c/ppb_var_array_buffer.h +include/ppapi/c/ppb_var_array.h +include/ppapi/c/ppb_var_dictionary.h include/ppapi/c/ppb_var.h include/ppapi/c/ppb_view.h include/ppapi/c/ppb_websocket.h @@ -380,22 +408,16 @@ include/ppapi/cpp/dev/file_chooser_dev.h include/ppapi/cpp/dev/find_dev.h include/ppapi/cpp/dev/font_dev.h include/ppapi/cpp/dev/graphics_2d_dev.h -include/ppapi/cpp/dev/host_resolver_dev.h include/ppapi/cpp/dev/ime_input_event_dev.h include/ppapi/cpp/dev/memory_dev.h -include/ppapi/cpp/dev/net_address_dev.h include/ppapi/cpp/dev/printing_dev.h include/ppapi/cpp/dev/resource_array_dev.h include/ppapi/cpp/dev/scriptable_object_deprecated.h include/ppapi/cpp/dev/scrollbar_dev.h include/ppapi/cpp/dev/selection_dev.h -include/ppapi/cpp/dev/tcp_socket_dev.h include/ppapi/cpp/dev/text_input_dev.h include/ppapi/cpp/dev/truetype_font_dev.h -include/ppapi/cpp/dev/udp_socket_dev.h include/ppapi/cpp/dev/url_util_dev.h -include/ppapi/cpp/dev/var_array_dev.h -include/ppapi/cpp/dev/var_dictionary_dev.h include/ppapi/cpp/dev/video_capture_client_dev.h include/ppapi/cpp/dev/video_capture_dev.h include/ppapi/cpp/dev/video_decoder_client_dev.h @@ -425,6 +447,7 @@ include/ppapi/cpp/graphics_2d.h include/ppapi/cpp/graphics_3d_client.h include/ppapi/c/pp_graphics_3d.h include/ppapi/cpp/graphics_3d.h +include/ppapi/cpp/host_resolver.h include/ppapi/cpp/image_data.h include/ppapi/c/pp_input_event.h include/ppapi/cpp/input_event.h @@ -440,6 +463,8 @@ include/ppapi/cpp/module.h include/ppapi/cpp/module_impl.h include/ppapi/cpp/mouse_cursor.h include/ppapi/cpp/mouse_lock.h +include/ppapi/cpp/net_address.h +include/ppapi/cpp/network_proxy.h include/ppapi/cpp/output_traits.h include/ppapi/cpp/pass_ref.h include/ppapi/c/ppp_graphics_3d.h @@ -457,13 +482,17 @@ include/ppapi/cpp/resource.h include/ppapi/c/pp_size.h include/ppapi/cpp/size.h include/ppapi/c/pp_stdint.h +include/ppapi/cpp/tcp_socket.h include/ppapi/c/pp_time.h include/ppapi/c/pp_touch_point.h include/ppapi/cpp/touch_point.h +include/ppapi/cpp/udp_socket.h include/ppapi/cpp/url_loader.h include/ppapi/cpp/url_request_info.h include/ppapi/cpp/url_response_info.h include/ppapi/cpp/var_array_buffer.h +include/ppapi/cpp/var_array.h +include/ppapi/cpp/var_dictionary.h include/ppapi/c/pp_var.h include/ppapi/cpp/var.h include/ppapi/cpp/view.h @@ -471,8 +500,10 @@ include/ppapi/cpp/websocket.h include/ppapi/gles2/gl2ext_ppapi.h include/ppapi/lib/gl/gles2/gl2ext_ppapi.h include/ppapi_simple/ps.h +include/ppapi_simple/ps_context_2d.h include/ppapi_simple/ps_event.h include/ppapi_simple/ps_instance.h +include/ppapi_simple/ps_interface.h include/ppapi_simple/ps_main.h include/ppapi/utility/completion_callback_factory.h include/ppapi/utility/completion_callback_factory_thread_traits.h @@ -488,9 +519,11 @@ include/ppapi/utility/websocket/websocket_api.h [win]include/win/pthread.h [win]include/win/sched.h [win]include/win/semaphore.h +include/sdk_util/atomicops.h include/sdk_util/auto_lock.h include/sdk_util/macros.h include/sdk_util/ref_object.h +include/sdk_util/scoped_ref.h include/sdk_util/thread_safe_queue.h include/sdk_util/thread_pool.h lib/glibc_x86_32/Debug/libjsoncpp.a @@ -550,12 +583,12 @@ lib/glibc_x86_64/Release/libppapi_simple.so lib/glibc_x86_64/Release/libsdk_util.a lib/glibc_x86_64/Release/libsdk_util.so [linux]lib/${PLATFORM}_host/Debug/libjsoncpp.a -[linux]lib/${PLATFORM}_host/Debug/libppapi.a +[linux,mac]lib/${PLATFORM}_host/Debug/libppapi.a [linux]lib/${PLATFORM}_host/Debug/libppapi_cpp.a [linux]lib/${PLATFORM}_host/Debug/libppapi_cpp_private.a [linux]lib/${PLATFORM}_host/Debug/libppapi_gles2.a [linux]lib/${PLATFORM}_host/Release/libjsoncpp.a -[linux]lib/${PLATFORM}_host/Release/libppapi.a +[linux,mac]lib/${PLATFORM}_host/Release/libppapi.a [linux]lib/${PLATFORM}_host/Release/libppapi_cpp.a [linux]lib/${PLATFORM}_host/Release/libppapi_cpp_private.a [linux]lib/${PLATFORM}_host/Release/libppapi_gles2.a @@ -565,6 +598,7 @@ lib/glibc_x86_64/Release/libsdk_util.so [win]lib/${PLATFORM}_x86_32_host/Debug/ppapi_cpp_private.lib [win]lib/${PLATFORM}_x86_32_host/Debug/ppapi_gles2.lib [win]lib/${PLATFORM}_x86_32_host/Debug/ppapi.lib +[win]lib/${PLATFORM}_x86_32_host/Debug/ppapi_simple.lib [win]lib/${PLATFORM}_x86_32_host/Debug/pthread.lib [win]lib/${PLATFORM}_x86_32_host/Release/jsoncpp.lib [win]lib/${PLATFORM}_x86_32_host/Release/nacl_io.lib @@ -572,6 +606,7 @@ lib/glibc_x86_64/Release/libsdk_util.so [win]lib/${PLATFORM}_x86_32_host/Release/ppapi_cpp_private.lib [win]lib/${PLATFORM}_x86_32_host/Release/ppapi_gles2.lib [win]lib/${PLATFORM}_x86_32_host/Release/ppapi.lib +[win]lib/${PLATFORM}_x86_32_host/Release/ppapi_simple.lib [win]lib/${PLATFORM}_x86_32_host/Release/pthread.lib [win]lib/${PLATFORM}_x86_32_host/Debug/sdk_util.lib [win]lib/${PLATFORM}_x86_32_host/Release/sdk_util.lib @@ -640,6 +675,7 @@ lib/pnacl/Release/libsdk_util.a LICENSE NOTICE README +README.Makefiles src/error_handling/error_handling.c [win]src/error_handling/make.bat src/error_handling/Makefile @@ -682,43 +718,81 @@ src/nacl_io/nacl_io.cc src/nacl_io/path.cc src/nacl_io/pepper_interface.cc src/nacl_io/real_pepper_interface.cc +src/ppapi_cpp/alarms_dev.cc src/ppapi_cpp/array_output.cc src/ppapi_cpp/audio.cc src/ppapi_cpp/audio_config.cc +src/ppapi_cpp/audio_input_dev.cc +src/ppapi_cpp/buffer_dev.cc src/ppapi_cpp/core.cc +src/ppapi_cpp/crypto_dev.cc +src/ppapi_cpp/cursor_control_dev.cc +src/ppapi_cpp/device_ref_dev.cc src/ppapi_cpp/directory_entry.cc +src/ppapi_cpp/event_base.cc +src/ppapi_cpp/events_dev.cc +src/ppapi_cpp/file_chooser_dev.cc src/ppapi_cpp/file_io.cc src/ppapi_cpp/file_ref.cc src/ppapi_cpp/file_system.cc +src/ppapi_cpp/find_dev.cc +src/ppapi_cpp/font_dev.cc src/ppapi_cpp/fullscreen.cc src/ppapi_cpp/graphics_2d.cc +src/ppapi_cpp/graphics_2d_dev.cc src/ppapi_cpp/graphics_3d.cc src/ppapi_cpp/graphics_3d_client.cc +src/ppapi_cpp/host_resolver.cc src/ppapi_cpp/image_data.cc +src/ppapi_cpp/ime_input_event_dev.cc src/ppapi_cpp/input_event.cc src/ppapi_cpp/instance.cc src/ppapi_cpp/instance_handle.cc src/ppapi_cpp/lock.cc -[win]src/ppapi_cpp/make.bat src/ppapi_cpp/Makefile +src/ppapi_cpp/memory_dev.cc src/ppapi_cpp/message_loop.cc src/ppapi_cpp/module.cc src/ppapi_cpp/mouse_cursor.cc src/ppapi_cpp/mouse_lock.cc +src/ppapi_cpp/net_address.cc +src/ppapi_cpp/network_proxy.cc src/ppapi_cpp/paint_aggregator.cc src/ppapi_cpp/paint_manager.cc src/ppapi_cpp/ppp_entrypoints.cc +src/ppapi_cpp/printing_dev.cc src/ppapi_cpp/rect.cc +src/ppapi_cpp/resource_array_dev.cc src/ppapi_cpp/resource.cc +src/ppapi_cpp/scriptable_object_deprecated.cc +src/ppapi_cpp/scrollbar_dev.cc +src/ppapi_cpp/selection_dev.cc src/ppapi_cpp/simple_thread.cc +src/ppapi_cpp/socket_dev.cc +src/ppapi_cpp/tcp_socket.cc +src/ppapi_cpp/text_input_dev.cc +src/ppapi_cpp/truetype_font_dev.cc +src/ppapi_cpp/udp_socket.cc src/ppapi_cpp/url_loader.cc src/ppapi_cpp/url_request_info.cc src/ppapi_cpp/url_response_info.cc +src/ppapi_cpp/url_util_dev.cc src/ppapi_cpp/var_array_buffer.cc +src/ppapi_cpp/var_array.cc src/ppapi_cpp/var.cc +src/ppapi_cpp/var_dictionary.cc +src/ppapi_cpp/video_capture_client_dev.cc +src/ppapi_cpp/video_capture_dev.cc +src/ppapi_cpp/video_decoder_client_dev.cc +src/ppapi_cpp/video_decoder_dev.cc src/ppapi_cpp/view.cc +src/ppapi_cpp/view_dev.cc src/ppapi_cpp/websocket_api.cc src/ppapi_cpp/websocket.cc +src/ppapi_cpp/widget_client_dev.cc +src/ppapi_cpp/widget_dev.cc +src/ppapi_cpp/zoom_dev.cc +[win]src/ppapi_cpp/make.bat src/ppapi_gles2/gl2ext_ppapi.c src/ppapi_gles2/gles2.c [win]src/ppapi_gles2/make.bat @@ -726,8 +800,10 @@ src/ppapi_gles2/Makefile [win]src/ppapi_simple/make.bat src/ppapi_simple/Makefile src/ppapi_simple/ps.cc +src/ppapi_simple/ps_context_2d.cc src/ppapi_simple/ps_event.cc src/ppapi_simple/ps_instance.cc +src/ppapi_simple/ps_interface.cc src/ppapi_simple/ps_main.cc src/ppapi_cpp_private/Makefile [win]src/ppapi_cpp_private/make.bat @@ -885,8 +961,8 @@ src/ppapi_cpp_private/x509_certificate_private.cc [win]src/pthread/signal.c [win]src/pthread/w32_CancelableWait.c [win]src/ppapi/make.bat -[win,linux]src/ppapi/Makefile -[win,linux]src/ppapi/ppapi_externs.c +src/ppapi/Makefile +src/ppapi/ppapi_externs.c src/sdk_util/Makefile [win]src/sdk_util/make.bat src/sdk_util/thread_pool.cc @@ -999,6 +1075,7 @@ tools/create_html.py tools/create_nmf.py tools/decode_dump.py [linux,mac]tools/dump_syms +tools/fix_deps.py [linux,mac]tools/minidump_stackwalk [linux,mac]tools/minidump_dump tools/genhttpfs.py diff --git a/native_client_sdk/src/build_tools/template.mk b/native_client_sdk/src/build_tools/template.mk index 19583eadd2..4d76ec5bdc 100644 --- a/native_client_sdk/src/build_tools/template.mk +++ b/native_client_sdk/src/build_tools/template.mk @@ -1,105 +1,68 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Copyright (c) 2013 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. -# -# GNU Make based build file. For details on GNU Make see: -# http://www.gnu.org/software/make/manual/make.html -# - - -# -# Default configuration -# -# By default we will build a Debug configuration using the GCC newlib toolchain -# to override this, specify TOOLCHAIN=newlib|glibc or CONFIG=Debug|Release on -# the make command-line or in this file prior to including common.mk. The -# toolchain we use by default will be the first valid one listed -VALID_TOOLCHAINS:={{' '.join(tools)}} +# GNU Makefile based on shared rules provided by the Native Client SDK. +# See README.Makefiles for more details. +VALID_TOOLCHAINS := {{' '.join(tools)}} {{pre}} -# -# Get pepper directory for toolchain and includes. -# -# If NACL_SDK_ROOT is not set, then assume it can be found relative to -# to this Makefile. -# -NACL_SDK_ROOT?=$(abspath $(CURDIR)/{{rel_sdk}}) +NACL_SDK_ROOT ?= $(abspath $(CURDIR)/{{rel_sdk}}) include $(NACL_SDK_ROOT)/tools/common.mk - -# -# Target Name -# -# The base name of the final NEXE, also the name of the NMF file containing -# the mapping between architecture and actual NEXE. -# -TARGET={{targets[0]['NAME']}} - -# -# List of sources to compile -# +TARGET = {{targets[0]['NAME']}} +[[if targets[0].get('DEPS'):]] +DEPS = {{' '.join(targets[0].get('DEPS', []))}} +LIBS = $(DEPS) {{' '.join(targets[0].get('LIBS'))}} +[[else:]] +LIBS = {{' '.join(targets[0].get('LIBS'))}} +[[]] [[for target in targets:]] -{{target['NAME']}}_SOURCES= \ -[[ for source in sorted(target['SOURCES']):]] -[[ if not source.endswith('.h'):]] - {{source}} \ +[[ source_list = (s for s in sorted(target['SOURCES']) if not s.endswith('.h'))]] +[[ source_list = ' \\\n '.join(source_list)]] +[[ sources = target['NAME'] + '_SOURCES']] +[[ cflags = target['NAME'] + '_CFLAGS']] +[[ flags = ' '.join(target.get('CCFLAGS', []))]] +[[ flags += ' '.join(target.get('CXXFLAGS', []))]] +[[ if len(targets) == 1:]] +[[ sources = 'SOURCES']] +[[ cflags = 'CFLAGS']] [[ ]] - +[[ if flags:]] +{{cflags}} = {{flags}} +[[ ]] +{{sources}} = {{source_list}} [[]] +# Build rules generated by macros from common.mk: -# -# List of libraries to link against. Unlike some tools, the GCC and LLVM -# based tools require libraries to be specified in the correct order. The -# order should be symbol reference followed by symbol definition, with direct -# sources to the link (object files) are left most. In this case: -# hello_world -> ppapi_main -> ppapi_cpp -> ppapi -> pthread -> libc -# Notice that libc is implied and come last through standard compiler/link -# switches. -# -# We break this list down into two parts, the set we need to rebuild (DEPS) -# and the set we do not. This example does not have a any additional library -# dependencies. -# -DEPS={{' '.join(targets[0].get('DEPS', []))}} -LIBS=$(DEPS) {{' '.join(targets[0].get('LIBS'))}} - - -# -# Use the library dependency macro for each dependency -# +[[if targets[0].get('DEPS'):]] $(foreach dep,$(DEPS),$(eval $(call DEPEND_RULE,$(dep)))) - -# -# Use the compile macro for each source. -# -[[for target in targets:]] -[[ name = target['NAME'] ]] -[[ flags = ' '.join(target.get('CCFLAGS', []))]] -[[ flags += ' '.join(target.get('CXXFLAGS', []))]] -$(foreach src,$({{name}}_SOURCES),$(eval $(call COMPILE_RULE,$(src),{{flags}}))) +[[if len(targets) > 1:]] +[[ for target in targets:]] +[[ name = target['NAME'] ]] +$(foreach src,$({{name}}_SOURCES),$(eval $(call COMPILE_RULE,$(src),$({{name}}_CFLAGS)))) +[[else:]] +$(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS)))) [[]] -# -# Use the link macro for this target on the list of sources. -# [[for target in targets:]] +[[ sources = target['NAME'] + '_SOURCES']] [[ name = target['NAME'] ]] +[[ if len(targets) == 1:]] +[[ sources = 'SOURCES']] +[[ name = '$(TARGET)']] [[ if target['TYPE'] == 'so':]] -$(eval $(call SO_RULE,{{name}},$({{name}}_SOURCES))) +$(eval $(call SO_RULE,{{name}},$({{sources}}))) [[ elif target['TYPE'] == 'so-standalone':]] -$(eval $(call SO_RULE,{{name}},$({{name}}_SOURCES),,,1)) +$(eval $(call SO_RULE,{{name}},$({{sources}}),,,1)) [[ else:]] ifeq ($(CONFIG),Release) -$(eval $(call LINK_RULE,{{name}}_unstripped,$({{name}}_SOURCES),$(LIBS),$(DEPS))) +$(eval $(call LINK_RULE,{{name}}_unstripped,$({{sources}}),$(LIBS),$(DEPS))) $(eval $(call STRIP_RULE,{{name}},{{name}}_unstripped)) else -$(eval $(call LINK_RULE,{{name}},$({{name}}_SOURCES),$(LIBS),$(DEPS))) +$(eval $(call LINK_RULE,{{name}},$({{sources}}),$(LIBS),$(DEPS))) endif [[]] -# -# Specify the NMF to be created with no additional arguments. -# $(eval $(call NMF_RULE,$(TARGET),)){{post}} diff --git a/native_client_sdk/src/build_tools/test_sdk.py b/native_client_sdk/src/build_tools/test_sdk.py index 0d8fc4a984..c2b7b7d4bb 100755 --- a/native_client_sdk/src/build_tools/test_sdk.py +++ b/native_client_sdk/src/build_tools/test_sdk.py @@ -25,20 +25,9 @@ sys.path.append(os.path.join(SDK_SRC_DIR, 'tools')) import getos -TEST_EXAMPLE_LIST = [ - 'nacl_io_test', -] - -TEST_LIBRARY_LIST = [ - 'gmock', - 'gtest', - 'gtest_ppapi', -] - - -def BuildStepBuildExamples(pepperdir, platform): +def BuildStepBuildExamples(pepperdir): for config in ('Debug', 'Release'): - build_sdk.BuildStepMakeAll(pepperdir, platform, 'examples', + build_sdk.BuildStepMakeAll(pepperdir, 'examples', 'Build Examples (%s)' % config, deps=False, config=config) @@ -54,18 +43,17 @@ def BuildStepCopyTests(pepperdir, toolchains, build_experimental): filters['EXPERIMENTAL'] = False tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, filters=filters) - platform = getos.GetPlatform() - build_projects.UpdateHelpers(pepperdir, platform, clobber=False) - build_projects.UpdateProjects(pepperdir, platform, tree, clobber=False, + build_projects.UpdateHelpers(pepperdir, clobber=False) + build_projects.UpdateProjects(pepperdir, tree, clobber=False, toolchains=toolchains) -def BuildStepBuildTests(pepperdir, platform): +def BuildStepBuildTests(pepperdir): for config in ('Debug', 'Release'): - build_sdk.BuildStepMakeAll(pepperdir, platform, 'testlibs', + build_sdk.BuildStepMakeAll(pepperdir, 'testlibs', 'Build Test Libraries (%s)' % config, config=config) - build_sdk.BuildStepMakeAll(pepperdir, platform, 'tests', + build_sdk.BuildStepMakeAll(pepperdir, 'tests', 'Build Tests (%s)' % config, deps=False, config=config) @@ -74,6 +62,7 @@ def main(args): parser = optparse.OptionParser() parser.add_option('--experimental', help='build experimental tests', action='store_true') + parser.add_option('--verbose', help='Verbose output', action='store_true') if 'NACL_SDK_ROOT' in os.environ: # We don't want the currently configured NACL_SDK_ROOT to have any effect @@ -82,15 +71,17 @@ def main(args): options, args = parser.parse_args(args[1:]) - platform = getos.GetPlatform() pepper_ver = str(int(build_version.ChromeMajorVersion())) pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver) toolchains = ['newlib', 'glibc', 'pnacl'] - toolchains.append(platform) + toolchains.append(getos.GetPlatform()) + + if options.verbose: + build_projects.verbose = True - BuildStepBuildExamples(pepperdir, platform) + BuildStepBuildExamples(pepperdir) BuildStepCopyTests(pepperdir, toolchains, options.experimental) - BuildStepBuildTests(pepperdir, platform) + BuildStepBuildTests(pepperdir) return 0 diff --git a/native_client_sdk/src/build_tools/tests/verify_filelist_test.py b/native_client_sdk/src/build_tools/tests/verify_filelist_test.py index a8ca2f51ee..a1da59150e 100755 --- a/native_client_sdk/src/build_tools/tests/verify_filelist_test.py +++ b/native_client_sdk/src/build_tools/tests/verify_filelist_test.py @@ -15,7 +15,7 @@ import verify_filelist def Verify(platform, rules_contents, directory_list): - rules = verify_filelist.Rules(platform, 'test', rules_contents) + rules = verify_filelist.Rules('test', platform, rules_contents) rules.VerifyDirectoryList(directory_list) diff --git a/native_client_sdk/src/build_tools/verify_filelist.py b/native_client_sdk/src/build_tools/verify_filelist.py index b97e4205ec..f43e99f9bb 100755 --- a/native_client_sdk/src/build_tools/verify_filelist.py +++ b/native_client_sdk/src/build_tools/verify_filelist.py @@ -32,15 +32,16 @@ class VerifyException(Exception): pass class Rules(object): - def __init__(self, platform, filename, contents=None): + def __init__(self, filename, platform=None, contents=None): self.glob_prefixes = [] self.exact_filenames = set() self.filename = filename - self.platform = platform - self.exe_ext = '.exe' if platform == 'win' else '' + self.platform = platform or getos.GetPlatform() + self.exe_ext = '.exe' if self.platform == 'win' else '' - if platform not in VALID_PLATFORMS: - raise ParseException(self.filename, 1, 'Unknown platform %s' % platform) + if self.platform not in VALID_PLATFORMS: + raise ParseException(self.filename, 1, + 'Unknown platform %s' % self.platform) if not contents: with open(filename) as f: @@ -141,8 +142,8 @@ def GetDirectoryList(directory_path): return result -def Verify(platform, rule_path, directory_path): - rules = Rules(platform, rule_path) +def Verify(rule_path, directory_path, platform=None): + rules = Rules(rule_path, platform=platform) directory_list = GetDirectoryList(directory_path) rules.VerifyDirectoryList(directory_list) @@ -164,7 +165,7 @@ def main(args): platform = getos.GetPlatform() try: - return Verify(platform, rule_path, directory_path) + return Verify(rule_path, directory_path, platform) except ParseException, e: print >> sys.stderr, 'Error parsing rules:\n', e return 1 diff --git a/native_client_sdk/src/documentation/Doxyfile b/native_client_sdk/src/documentation/Doxyfile deleted file mode 100644 index 54b8213b28..0000000000 --- a/native_client_sdk/src/documentation/Doxyfile +++ /dev/null @@ -1,1663 +0,0 @@ -# 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 deleted file mode 100644 index b4751f83a1..0000000000 --- a/native_client_sdk/src/documentation/build.scons +++ /dev/null @@ -1,28 +0,0 @@ -#! -*- python -*- -# -# Copyright (c) 2010 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. - -"""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 deleted file mode 100755 index ce3b49f4c2..0000000000 --- a/native_client_sdk/src/documentation/check.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/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 deleted file mode 100644 index 8a944c13e4..0000000000 --- a/native_client_sdk/src/documentation/footer.dox +++ /dev/null @@ -1,22 +0,0 @@ - <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&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> - ©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 deleted file mode 100644 index a261dd3170..0000000000 --- a/native_client_sdk/src/documentation/header.dox +++ /dev/null @@ -1,14 +0,0 @@ -<!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 deleted file mode 100644 index 9a9e18c725..0000000000 --- a/native_client_sdk/src/documentation/images-dox/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 2af602e99f..0000000000 --- a/native_client_sdk/src/documentation/index.dox +++ /dev/null @@ -1,46 +0,0 @@ -/** @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 deleted file mode 100644 index 3923ccb5e9..0000000000 --- a/native_client_sdk/src/documentation/modules.dox +++ /dev/null @@ -1,22 +0,0 @@ -// 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 deleted file mode 100644 index 81d1a4251c..0000000000 --- a/native_client_sdk/src/documentation/stylesheet-dox.css +++ /dev/null @@ -1,478 +0,0 @@ -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 deleted file mode 100644 index 3d17af00ca..0000000000 --- a/native_client_sdk/src/documentation/stylesheet.css +++ /dev/null @@ -1,101 +0,0 @@ -@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/api/file_io/example.js b/native_client_sdk/src/examples/api/file_io/example.js index 1183c9cd43..37be1cb47f 100644 --- a/native_client_sdk/src/examples/api/file_io/example.js +++ b/native_client_sdk/src/examples/api/file_io/example.js @@ -2,88 +2,151 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +function moduleDidLoad() { + common.hideModule(); +} + // Called by the common.js module. function domContentLoaded(name, tc, config, width, height) { - window.webkitStorageInfo.requestQuota(window.PERSISTENT, 1024*1024, - function(bytes) { - common.updateStatus( - 'Allocated '+bytes+' bytes of persistant storage.'); - common.createNaClModule(name, tc, config, width, height); - common.attachDefaultListeners(); - }, - function(e) { alert('Failed to allocate space') }); + navigator.webkitPersistentStorage.requestQuota(1024 * 1024, + function(bytes) { + common.updateStatus( + 'Allocated ' + bytes + ' bytes of persistant storage.'); + common.createNaClModule(name, tc, config, width, height); + common.attachDefaultListeners(); + }, + function(e) { alert('Failed to allocate space') }); } // Called by the common.js module. function attachListeners() { - document.getElementById('saveButton').addEventListener('click', saveFile); - document.getElementById('loadButton').addEventListener('click', loadFile); - document.getElementById('deleteButton').addEventListener('click', deleteFile); - document.getElementById('listButton').addEventListener('click', listDir); + var radioEls = document.querySelectorAll('input[type="radio"]'); + for (var i = 0; i < radioEls.length; ++i) { + radioEls[i].addEventListener('click', onRadioClicked); + } + + function addEventListenerToButton(parentId, func) { + document.querySelector('#' + parentId + ' button') + .addEventListener('click', func); + } + + addEventListenerToButton('saveFile', saveFile); + addEventListenerToButton('loadFile', loadFile); + addEventListenerToButton('delete', deleteFileOrDirectory); + addEventListenerToButton('listDir', listDir); + addEventListenerToButton('makeDir', makeDir); } -function loadFile() { - if (common.naclModule) { - var fileName = document.getElementById('fileName').value; +function onRadioClicked(e) { + var divId = this.id.slice(6); // skip "radio_" + var functionEls = document.querySelectorAll('.function'); + for (var i = 0; i < functionEls.length; ++i) { + var visible = functionEls[i].id === divId; + if (functionEls[i].id === divId) + functionEls[i].removeAttribute('hidden'); + else + functionEls[i].setAttribute('hidden'); + } +} - // Package a message using a simple protocol containing: - // instruction file_name_length file_name - var msg = "ld " + fileName.length + " " + fileName; - common.naclModule.postMessage(msg); +function makeMessage(command, path) { + // Package a message using a simple protocol containing: + // command <path length> <path> <space-separated extra args> + var msg = command; + msg += ' '; + msg += path.length; + msg += ' '; + msg += path; + // Maybe add extra args + for (var i = 2; i < arguments.length; ++i) { + msg += ' ' + arguments[i]; } + return msg; } function saveFile() { if (common.naclModule) { - var fileName = document.getElementById('fileName').value; - var fileText = document.getElementById('fileEditor').value; - - // Package a message using a simple protocol containing: - // instruction file_name_length file_name file_contents - var msg = "sv " + fileName.length + " " + fileName + " " + fileText; - common.naclModule.postMessage(msg); + var fileName = document.querySelector('#saveFile input').value; + var fileText = document.querySelector('#saveFile textarea').value; + common.naclModule.postMessage(makeMessage('sv', fileName, fileText)); + // clear the editor. + fileText.value = ''; } } -function deleteFile() { +function loadFile() { if (common.naclModule) { - var fileName = document.getElementById('fileName').value; + var fileName = document.querySelector('#loadFile input').value; + // clear the editor first (in case there is an error and there is no + // output). + document.querySelector('#loadFile textarea').value = ''; + common.naclModule.postMessage(makeMessage('ld', fileName)); + } +} - // Package a message using a simple protocol containing: - // instruction file_name_length file_name - var msg = "de " + fileName.length + " " + fileName; - common.naclModule.postMessage(msg); +function deleteFileOrDirectory() { + if (common.naclModule) { + var fileName = document.querySelector('#delete input').value; + common.naclModule.postMessage(makeMessage('de', fileName)); } } function listDir() { if (common.naclModule) { - var dirName = document.getElementById('dirName').value; + var dirName = document.querySelector('#listDir input').value; + common.naclModule.postMessage(makeMessage('ls', dirName)); + } +} - // Package a message using a simple protocol containing: - // instruction file_name_length file_name - var msg = "ls " + dirName.length + " " + dirName; - common.naclModule.postMessage(msg); +function makeDir() { + if (common.naclModule) { + var dirName = document.querySelector('#makeDir input').value; + common.naclModule.postMessage(makeMessage('md', dirName)); } } // Called by the common.js module. function handleMessage(message_event) { - var messageParts = message_event.data.split("|", 3); + var msg = message_event.data; + var parts = msg.split('|'); + var command = parts[0]; + var args = parts.slice(1); - if (messageParts[0] == "ERR") { - common.updateStatus(messageParts[1]); - document.getElementById('statusField').style.color = "red"; - } - else if(messageParts[0] == "STAT") { - common.updateStatus(messageParts[1]); - } - else if (messageParts[0] == "DISP") { - // Display the message in the file edit box - document.getElementById('fileEditor').value = messageParts[1]; - } - else if (messageParts[0] == "READY") { - var statusField = document.getElementById('statusField'); - common.updateStatus(statusField.innerHTML + ' Ready!'); + if (command == 'ERR') { + common.logMessage('Error: ' + args[0] + '\n'); + } else if (command == 'STAT') { + common.logMessage(args[0] + '\n'); + } else if (command == 'READY') { + common.logMessage('Filesystem ready!\n'); + } else if (command == 'DISP') { + // Find the file editor that is currently visible. + var fileEditorEl = + document.querySelector('.function:not([hidden]) > textarea'); + // Rejoin args with pipe (|) -- there is only one argument, and it can + // contain the pipe character. + fileEditorEl.value = args.join('|'); + } else if (command == 'LIST') { + var listDirOutputEl = document.getElementById('listDirOutput'); + + // NOTE: files with | in their names will be incorrectly split. Fixing this + // is left as an exercise for the reader. + + // Remove all children of this element... + while (listDirOutputEl.firstChild) { + listDirOutputEl.removeChild(listDirOutputEl.firstChild); + } + + if (args.length) { + // Add new <li> elements for each file. + for (var i = 0; i < args.length; ++i) { + var itemEl = document.createElement('li'); + itemEl.textContent = args[i]; + listDirOutputEl.appendChild(itemEl); + } + } else { + var itemEl = document.createElement('li'); + itemEl.textContent = '<empty directory>'; + listDirOutputEl.appendChild(itemEl); + } } } diff --git a/native_client_sdk/src/examples/api/file_io/file_io.cc b/native_client_sdk/src/examples/api/file_io/file_io.cc index d8e27afd0f..cf8be2689b 100644 --- a/native_client_sdk/src/examples/api/file_io/file_io.cc +++ b/native_client_sdk/src/examples/api/file_io/file_io.cc @@ -41,6 +41,7 @@ const char* const kLoadPrefix = "ld"; const char* const kSavePrefix = "sv"; const char* const kDeletePrefix = "de"; const char* const kListPrefix = "ls"; +const char* const kMakeDirPrefix = "md"; } /// The Instance class. One of these exists for each instance of your NaCl @@ -115,41 +116,35 @@ class FileIoInstance : public pp::Instance { } // Dispatch the instruction - if (instruction.compare(kLoadPrefix) == 0) { + if (instruction == kLoadPrefix) { file_thread_.message_loop().PostWork( callback_factory_.NewCallback(&FileIoInstance::Load, file_name)); - return; - } - - if (instruction.compare(kSavePrefix) == 0) { + } else if (instruction == kSavePrefix) { // Read the rest of the message as the file text reader.ignore(1); // Eat the delimiter std::string file_text = message.substr(reader.tellg()); file_thread_.message_loop().PostWork(callback_factory_.NewCallback( &FileIoInstance::Save, file_name, file_text)); - return; - } - - if (instruction.compare(kDeletePrefix) == 0) { + } else if (instruction == kDeletePrefix) { file_thread_.message_loop().PostWork( callback_factory_.NewCallback(&FileIoInstance::Delete, file_name)); - return; - } - - if (instruction.compare(kListPrefix) == 0) { + } else if (instruction == kListPrefix) { const std::string& dir_name = file_name; file_thread_.message_loop().PostWork( callback_factory_.NewCallback(&FileIoInstance::List, dir_name)); - return; + } else if (instruction == kMakeDirPrefix) { + const std::string& dir_name = file_name; + file_thread_.message_loop().PostWork( + callback_factory_.NewCallback(&FileIoInstance::MakeDir, dir_name)); } } void OpenFileSystem(int32_t /* result */) { - int32_t rv = file_system_.Open(1024 * 1024, pp::CompletionCallback()); + int32_t rv = file_system_.Open(1024 * 1024, pp::BlockUntilComplete()); if (rv == PP_OK) { file_system_ready_ = true; // Notify the user interface that we're ready - PostMessage(pp::Var("READY|")); + PostMessage("READY|"); } else { ShowErrorMessage("Failed to open file system", rv); } @@ -169,7 +164,7 @@ class FileIoInstance : public pp::Instance { file.Open(ref, PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE, - pp::CompletionCallback()); + pp::BlockUntilComplete()); if (open_result != PP_OK) { ShowErrorMessage("File open for write failed", open_result); return; @@ -188,7 +183,7 @@ class FileIoInstance : public pp::Instance { bytes_written = file.Write(offset, file_contents.data() + offset, file_contents.length(), - pp::CompletionCallback()); + pp::BlockUntilComplete()); if (bytes_written > 0) { offset += bytes_written; } else { @@ -198,7 +193,7 @@ class FileIoInstance : public pp::Instance { } while (bytes_written < static_cast<int64_t>(file_contents.length())); } // All bytes have been written, flush the write buffer to complete - int32_t flush_result = file.Flush(pp::CompletionCallback()); + int32_t flush_result = file.Flush(pp::BlockUntilComplete()); if (flush_result != PP_OK) { ShowErrorMessage("File fail to flush", flush_result); return; @@ -215,7 +210,7 @@ class FileIoInstance : public pp::Instance { pp::FileIO file(this); int32_t open_result = - file.Open(ref, PP_FILEOPENFLAG_READ, pp::CompletionCallback()); + file.Open(ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete()); if (open_result == PP_ERROR_FILENOTFOUND) { ShowStatusMessage("File not found"); return; @@ -224,7 +219,7 @@ class FileIoInstance : public pp::Instance { return; } PP_FileInfo info; - int32_t query_result = file.Query(&info, pp::CompletionCallback()); + int32_t query_result = file.Query(&info, pp::BlockUntilComplete()); if (query_result != PP_OK) { ShowErrorMessage("File query failed", query_result); return; @@ -242,7 +237,7 @@ class FileIoInstance : public pp::Instance { bytes_read = file.Read(offset, &data[offset], data.size() - offset, - pp::CompletionCallback()); + pp::BlockUntilComplete()); if (bytes_read > 0) offset += bytes_read; } while (bytes_read > 0); @@ -254,7 +249,7 @@ class FileIoInstance : public pp::Instance { PP_DCHECK(bytes_read == 0); // Done reading, send content to the user interface std::string string_data(data.begin(), data.end()); - PostMessage(pp::Var("DISP|" + string_data)); + PostMessage("DISP|" + string_data); ShowStatusMessage("Load complete"); } @@ -265,15 +260,15 @@ class FileIoInstance : public pp::Instance { } pp::FileRef ref(file_system_, file_name.c_str()); - int32_t result = ref.Delete(pp::CompletionCallback()); + int32_t result = ref.Delete(pp::BlockUntilComplete()); if (result == PP_ERROR_FILENOTFOUND) { - ShowStatusMessage("File not found"); + ShowStatusMessage("File/Directory not found"); return; } else if (result != PP_OK) { ShowErrorMessage("Deletion failed", result); return; } - ShowStatusMessage("File deleted"); + ShowStatusMessage("File/Directory deleted"); } void List(int32_t /* result */, const std::string& dir_name) { @@ -281,6 +276,7 @@ class FileIoInstance : public pp::Instance { ShowErrorMessage("File system is not open", PP_ERROR_FAILED); return; } + pp::FileRef ref(file_system_, dir_name.c_str()); // Pass ref along to keep it alive. @@ -296,27 +292,44 @@ class FileIoInstance : public pp::Instance { return; } - std::string buffer = "File list:"; + std::stringstream ss; + ss << "LIST"; for (size_t i = 0; i < entries.size(); ++i) { pp::Var name = entries[i].file_ref().GetName(); - if (name.is_string()) - buffer += " " + name.AsString(); + if (name.is_string()) { + ss << "|" << name.AsString(); + } + } + PostMessage(ss.str()); + } + + void MakeDir(int32_t /* result */, const std::string& dir_name) { + if (!file_system_ready_) { + ShowErrorMessage("File system is not open", PP_ERROR_FAILED); + return; + } + pp::FileRef ref(file_system_, dir_name.c_str()); + + int32_t result = ref.MakeDirectory(pp::BlockUntilComplete()); + if (result != PP_OK) { + ShowErrorMessage("Make directory failed", result); + return; } - ShowStatusMessage(buffer); + ShowStatusMessage("Made directory"); } /// Encapsulates our simple javascript communication protocol void ShowErrorMessage(const std::string& message, int32_t result) { std::stringstream ss; ss << "ERR|" << message << " -- Error #: " << result; - PostMessage(pp::Var(ss.str())); + PostMessage(ss.str()); } /// Encapsulates our simple javascript communication protocol void ShowStatusMessage(const std::string& message) { std::stringstream ss; ss << "STAT|" << message; - PostMessage(pp::Var(ss.str())); + PostMessage(ss.str()); } }; diff --git a/native_client_sdk/src/examples/api/file_io/index.html b/native_client_sdk/src/examples/api/file_io/index.html index 7c62ebdd9c..f08a4d09c4 100644 --- a/native_client_sdk/src/examples/api/file_io/index.html +++ b/native_client_sdk/src/examples/api/file_io/index.html @@ -18,21 +18,65 @@ <p>The File IO example demonstrates saving, loading, and deleting files from the persistent file store.</p> - <textarea id="fileEditor" - cols="40" - rows="10" - wrap="hard" - placeholder="Enter some text to save in a file..."></textarea> - <br>File Name - <input type="text" id="fileName" action="" value="/filename.txt"> - <button id="saveButton" action="">Save</button> - <button id="loadButton" action="">Load</button> - <button id="deleteButton" action="">Delete</button> - - <br>Directory Name - <input type="text" id="dirName" action="" value="/"> - <button id="listButton" action="">List</button> + <div> + <span> + <input type="radio" id="radio_saveFile" name="group" checked="checked"> + Save File + <input type="radio" id="radio_loadFile" name="group">Load File + <input type="radio" id="radio_delete" name="group">Delete File/Directory + <input type="radio" id="radio_listDir" name="group">List Directory + <input type="radio" id="radio_makeDir" name="group">Make Directory + </span> + </div> + <div class="function" id="saveFile"> + <div> + <span> + Filename: + <input type="text" value="/filename.txt"> + <button>Save</button> + </span> + </div> + <textarea id="saveFileEditor" cols="40" rows="10" wrap="hard" + placeholder="Enter some text to save in a file..."></textarea> + </div> + <div class="function" id="loadFile" hidden> + <div> + <span> + Filename: + <input type="text" value="/filename.txt"> + <button>Load</button> + </span> + </div> + <textarea cols="40" rows="10" wrap="hard"></textarea> + </div> + <div class="function" id="delete" hidden> + <span> + Filename/Directory: + <input type="text" value="/filename.txt"> + <button>Delete</button> + </span> + </div> + <div class="function" id="listDir" hidden> + <div> + <span> + Directory: + <input type="text" value="/"> + <button>List Directory</button> + </span> + </div> + Result: + <ul id="listDirOutput"> + </ul> + </div> + <div class="function" id="makeDir" hidden> + <span> + Directory: + <input type="text" value="/directory"> + <button>Make Directory</button> + </span> + </div> + <pre id="log" style="font-weight: bold"></pre> <!-- The NaCl plugin will be embedded inside the element with id "listener". See common.js.--> <div id="listener"></div> diff --git a/native_client_sdk/src/examples/api/input_event/index.html b/native_client_sdk/src/examples/api/input_event/index.html index 2e97b9884b..0739c7ac6c 100644 --- a/native_client_sdk/src/examples/api/input_event/index.html +++ b/native_client_sdk/src/examples/api/input_event/index.html @@ -19,7 +19,7 @@ found in the LICENSE file. 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 + CallOnMainThread so that PostMessage can send the result of the worker thread to the browser.</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 diff --git a/native_client_sdk/src/examples/api/url_loader/example.js b/native_client_sdk/src/examples/api/url_loader/example.js index 2ebdfeb29e..9c62dce42f 100644 --- a/native_client_sdk/src/examples/api/url_loader/example.js +++ b/native_client_sdk/src/examples/api/url_loader/example.js @@ -15,7 +15,7 @@ function attachListeners() { } function loadUrl() { - common.naclModule.postMessage('getUrl:geturl_success.html'); + common.naclModule.postMessage('getUrl:url_loader_success.html'); } // Called by the common.js module. diff --git a/native_client_sdk/src/examples/api/websocket/example.js b/native_client_sdk/src/examples/api/websocket/example.js index 561d044e32..ed0b9274e9 100644 --- a/native_client_sdk/src/examples/api/websocket/example.js +++ b/native_client_sdk/src/examples/api/websocket/example.js @@ -26,7 +26,8 @@ function doConnect(event) { function doSend() { // Send a request message. See also websocket.cc for the request format. var message = document.getElementById('message').value; - common.naclModule.postMessage('s;' + message); + var type = document.getElementById('is_binary').checked ? 'b;' : 't;'; + common.naclModule.postMessage(type + message); event.preventDefault(); } diff --git a/native_client_sdk/src/examples/api/websocket/index.html b/native_client_sdk/src/examples/api/websocket/index.html index f077fcdc7e..e7caa26b17 100644 --- a/native_client_sdk/src/examples/api/websocket/index.html +++ b/native_client_sdk/src/examples/api/websocket/index.html @@ -23,13 +23,14 @@ found in the LICENSE file. </p> <form id="connectForm"> <input type="text" id="url" style="width: 400px" - value="ws://html5rocks.websocket.org/echo?encoding=text"> + value="ws://html5rocks.websocket.org/echo"> <input type="submit" value="Connect"> </form> <form id="sendForm"> <input type="text" id="message" value="hello" style="width: 400px"> <input type="submit" value="Send"> + <input type="checkbox" id="type">as a binary message </form> <button id="closeButton">Close</button> diff --git a/native_client_sdk/src/examples/api/websocket/websocket.cc b/native_client_sdk/src/examples/api/websocket/websocket.cc index bc5949b794..be994c5817 100644 --- a/native_client_sdk/src/examples/api/websocket/websocket.cc +++ b/native_client_sdk/src/examples/api/websocket/websocket.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <stdio.h> +#include <sstream> + #include "ppapi/cpp/completion_callback.h" #include "ppapi/cpp/instance.h" #include "ppapi/cpp/module.h" @@ -21,7 +24,8 @@ class WebSocketInstance : public pp::Instance { void Open(const std::string& url); void Close(); - void Send(const std::string& message); + void SendAsBinary(const std::string& message); + void SendAsText(const std::string& message); void Receive(); void OnConnectCompletion(int32_t result); @@ -36,6 +40,23 @@ class WebSocketInstance : public pp::Instance { pp::Var receive_var_; }; +#define MAX_TO_CONVERT 8 +#define BYTES_PER_CHAR 4 +#define TAIL_AND_NUL_SIZE 4 + +static std::string ArrayToString(pp::VarArrayBuffer& array) { + char tmp[MAX_TO_CONVERT * BYTES_PER_CHAR + TAIL_AND_NUL_SIZE]; + uint32_t offs = 0; + uint8_t* data = static_cast<uint8_t*>(array.Map()); + + for (offs = 0; offs < array.ByteLength() && offs < MAX_TO_CONVERT; offs++) + sprintf(&tmp[offs * BYTES_PER_CHAR], "%02Xh ", data[offs]); + + sprintf(&tmp[offs * BYTES_PER_CHAR], "..."); + array.Unmap(); + return std::string(tmp); +} + void WebSocketInstance::HandleMessage(const pp::Var& var_message) { if (!var_message.is_string()) return; @@ -54,10 +75,15 @@ void WebSocketInstance::HandleMessage(const pp::Var& var_message) { // The command 'c' requests to close without any argument like "c;" Close(); break; - case 's': - // The command 's' requests to send a message as a text frame. The message - // is passed as an argument like "s;message". - Send(message.substr(2)); + case 'b': + // The command 'b' requests to send a message as a binary frame. The + // message is passed as an argument like "b;message". + SendAsBinary(message.substr(2)); + break; + case 't': + // The command 't' requests to send a message as a text frame. The message + // is passed as an argument like "t;message". + SendAsText(message.substr(2)); break; } } @@ -87,11 +113,25 @@ void WebSocketInstance::Close() { PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var("bye"), callback); } -void WebSocketInstance::Send(const std::string& message) { +void WebSocketInstance::SendAsBinary(const std::string& message) { + if (!IsConnected()) + return; + uint32_t size = message.size(); + pp::VarArrayBuffer array_buffer(size); + char* data = static_cast<char*>(array_buffer.Map()); + for (uint32_t i = 0; i < size; ++i) + data[i] = message[i]; + array_buffer.Unmap(); + websocket_->SendMessage(array_buffer); + std::string message_text = ArrayToString(array_buffer); + PostMessage(pp::Var("send (binary): " + message_text)); +} + +void WebSocketInstance::SendAsText(const std::string& message) { if (!IsConnected()) return; websocket_->SendMessage(pp::Var(message)); - PostMessage(pp::Var(std::string("send: ") + message)); + PostMessage(pp::Var("send (text): " + message)); } void WebSocketInstance::Receive() { @@ -116,10 +156,14 @@ void WebSocketInstance::OnCloseCompletion(int32_t result) { void WebSocketInstance::OnReceiveCompletion(int32_t result) { if (result == PP_OK) { - if (receive_var_.is_array_buffer()) - PostMessage(pp::Var("receive: binary data")); - else - PostMessage(pp::Var(std::string("receive: ") + receive_var_.AsString())); + if (receive_var_.is_array_buffer()) { + pp::VarArrayBuffer array_buffer(receive_var_); + std::string message_text = ArrayToString(array_buffer); + PostMessage("receive (binary): " + message_text); + } + else { + PostMessage("receive (text): " + receive_var_.AsString()); + } } Receive(); } diff --git a/native_client_sdk/src/examples/demo/earth/earth.cc b/native_client_sdk/src/examples/demo/earth/earth.cc new file mode 100644 index 0000000000..d8e35462a5 --- /dev/null +++ b/native_client_sdk/src/examples/demo/earth/earth.cc @@ -0,0 +1,871 @@ +// Copyright (c) 2013 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. + +#include <assert.h> +#include <math.h> +#include <ppapi/c/pp_point.h> +#include <ppapi/c/ppb_input_event.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/module.h> +#include <ppapi/cpp/rect.h> +#include <ppapi/cpp/size.h> +#include <ppapi/cpp/var.h> +#include <ppapi/cpp/var_array.h> +#include <ppapi/cpp/var_array_buffer.h> +#include <ppapi/cpp/var_dictionary.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <unistd.h> + +#include <algorithm> +#include <string> + +#include "sdk_util/macros.h" +#include "sdk_util/thread_pool.h" + +// Global properties used to setup Earth demo. +namespace { +const float kHugeZ = 1.0e38f; +const float kPI = M_PI; +const float kTwoPI = kPI * 2.0f; +const float kOneOverPI = 1.0f / kPI; +const float kOneOver2PI = 1.0f / kTwoPI; +const float kOneOver255 = 1.0f / 255.0f; +const int kArcCosineTableSize = 4096; +const int kFramesToBenchmark = 100; +const float kZoomMin = 1.0f; +const float kZoomMax = 50.0f; +const float kWheelSpeed = 2.0f; +const float kLightMin = 0.0f; +const float kLightMax = 2.0f; +const int kFrameTimeBufferSize = 512; + +// Timer helper for benchmarking. Returns seconds elapsed since program start, +// as a double. +timeval start_tv; +int start_tv_retv = gettimeofday(&start_tv, NULL); + +inline double getseconds() { + const double usec_to_sec = 0.000001; + timeval tv; + if ((0 == start_tv_retv) && (0 == gettimeofday(&tv, NULL))) + return (tv.tv_sec - start_tv.tv_sec) + tv.tv_usec * usec_to_sec; + return 0.0; +} + +// RGBA helper functions. +inline float ExtractR(uint32_t c) { + return static_cast<float>(c & 0xFF) * kOneOver255; +} + +inline float ExtractG(uint32_t c) { + return static_cast<float>((c & 0xFF00) >> 8) * kOneOver255; +} + +inline float ExtractB(uint32_t c) { + return static_cast<float>((c & 0xFF0000) >> 16) * kOneOver255; +} + +inline uint32_t MakeRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { + return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)); +} + +// simple container for earth texture +struct Texture { + int width, height; + uint32_t* pixels; + Texture(int w, int h) : width(w), height(h) { + pixels = new uint32_t[w * h]; + memset(pixels, 0, sizeof(uint32_t) * w * h); + } + explicit Texture(int w, int h, uint32_t* p) : width(w), height(h) { + pixels = new uint32_t[w * h]; + memcpy(pixels, p, sizeof(uint32_t) * w * h); + } + ~Texture() { delete[] pixels; } + + DISALLOW_COPY_AND_ASSIGN(Texture); +}; + + + +struct ArcCosine { + // slightly larger table so we can interpolate beyond table size + float table[kArcCosineTableSize + 2]; + float TableLerp(float x); + ArcCosine(); +}; + +ArcCosine::ArcCosine() { + // build a slightly larger table to allow for numeric imprecision + for (int i = 0; i < (kArcCosineTableSize + 2); ++i) { + float f = static_cast<float>(i) / kArcCosineTableSize; + f = f * 2.0f - 1.0f; + table[i] = acos(f); + } +} + +// looks up acos(f) using a table and lerping between entries +// (it is expected that input f is between -1 and 1) +float ArcCosine::TableLerp(float f) { + float x = (f + 1.0f) * 0.5f; + x = x * kArcCosineTableSize; + int ix = static_cast<int>(x); + float fx = static_cast<float>(ix); + float dx = x - fx; + float af = table[ix]; + float af2 = table[ix + 1]; + return af + (af2 - af) * dx; +} + +// Helper functions for quick but approximate sqrt. +union Convert { + float f; + int i; + Convert(int x) { i = x; } + Convert(float x) { f = x; } + int AsInt() { return i; } + float AsFloat() { return f; } +}; + +inline const int AsInteger(const float f) { + Convert u(f); + return u.AsInt(); +} + +inline const float AsFloat(const int i) { + Convert u(i); + return u.AsFloat(); +} + +const long int kOneAsInteger = AsInteger(1.0f); +const float kScaleUp = float(0x00800000); +const float kScaleDown = 1.0f / kScaleUp; + +inline float inline_quick_sqrt(float x) { + int i; + i = (AsInteger(x) >> 1) + (kOneAsInteger >> 1); + return AsFloat(i); +} + +inline float inline_sqrt(float x) { + float y; + y = inline_quick_sqrt(x); + y = (y * y + x) / (2.0f * y); + y = (y * y + x) / (2.0f * y); + return y; +} + +// takes a -0..1+ color, clamps it to 0..1 and maps it to 0..255 integer +inline uint32_t Clamp255(float x) { + if (x < 0.0f) { + x = 0.0f; + } else if (x > 1.0f) { + x = 1.0f; + } + return static_cast<uint32_t>(x * 255.0f); +} +} // namespace + + +// The main object that runs the Earth demo. +class Planet : public pp::Instance { + public: + explicit Planet(PP_Instance instance); + virtual ~Planet(); + + virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); + + virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip); + + // Catch events. + virtual bool HandleInputEvent(const pp::InputEvent& event); + + // Catch messages posted from Javascript. + virtual void HandleMessage(const pp::Var& message); + + private: + // Methods prefixed with 'w' are run on worker threads. + uint32_t* wGetAddr(int x, int y); + void wRenderPixelSpan(int x0, int x1, int y); + void wMakeRect(int r, int *x, int *y, int *w, int *h); + void wRenderRect(int x0, int y0, int x1, int y1); + void wRenderRegion(int region); + static void wRenderRegionEntry(int region, void *thiz); + + // These methods are only called by the main thread. + void CacheCalcs(); + void SetPlanetXYZR(float x, float y, float z, float r); + void SetPlanetPole(float x, float y, float z); + void SetPlanetEquator(float x, float y, float z); + void SetPlanetSpin(float x, float y); + void SetEyeXYZ(float x, float y, float z); + void SetLightXYZ(float x, float y, float z); + void SetAmbientRGB(float r, float g, float b); + void SetDiffuseRGB(float r, float g, float b); + void SetZoom(float zoom); + void SetLight(float zoom); + void SetTexture(const std::string& name, int width, int height, + uint32_t* pixels); + + void Reset(); + void PostInit(int32_t result); + void UpdateSim(); + void Render(); + void Draw(); + void StartBenchmark(); + void EndBenchmark(); + + // Runs a tick of the simulations, updating all buffers. Flushes the + // contents of |image_data_| to the 2D graphics context. + void Update(); + + // Post a small key-value message to update JS. + void PostUpdateMessage(const char* message_name, double value); + // 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. + void FlushPixelBuffer(); + static void FlushCallback(void* data, int32_t result); + + // User Interface settings. These settings are controlled via html + // controls or via user input. + float ui_light_; + float ui_zoom_; + float ui_spin_x_; + float ui_spin_y_; + + // Various settings for position & orientation of planet. Do not change + // these variables, instead use SetPlanet*() functions. + float planet_radius_; + float planet_spin_x_; + float planet_spin_y_; + float planet_x_, planet_y_, planet_z_; + float planet_pole_x_, planet_pole_y_, planet_pole_z_; + float planet_equator_x_, planet_equator_y_, planet_equator_z_; + + // Observer's eye. Do not change these variables, instead use SetEyeXYZ(). + float eye_x_, eye_y_, eye_z_; + + // Light position, ambient and diffuse settings. Do not change these + // variables, instead use SetLightXYZ(), SetAmbientRGB() and SetDiffuseRGB(). + float light_x_, light_y_, light_z_; + float diffuse_r_, diffuse_g_, diffuse_b_; + float ambient_r_, ambient_g_, ambient_b_; + + // Cached calculations. Do not change these variables - they are updated by + // CacheCalcs() function. + float planet_xyz_; + float planet_pole_x_equator_x_; + float planet_pole_x_equator_y_; + float planet_pole_x_equator_z_; + float planet_radius2_; + float planet_one_over_radius_; + float eye_xyz_; + + // Source texture (earth map). + Texture* base_tex_; + Texture* night_tex_; + int width_for_tex_; + int height_for_tex_; + std::string name_for_tex_; + + // Quick ArcCos helper. + ArcCosine acos_; + + // Misc. + pp::Graphics2D* graphics_2d_context_; + pp::ImageData* image_data_; + int num_threads_; + int num_regions_; + ThreadPool* workers_; + int width_; + int height_; + uint32_t stride_in_pixels_; + uint32_t* pixel_buffer_; + int benchmark_frame_counter_; + bool benchmarking_; + double benchmark_start_time_; + double benchmark_end_time_; +}; + + +bool Planet::Init(uint32_t argc, const char* argn[], const char* argv[]) { + // Request PPAPI input events for mouse & keyboard. + RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE); + RequestInputEvents(PP_INPUTEVENT_CLASS_WHEEL); + RequestInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); + // Request a set of images from JS. After images are loaded by JS, a + // message from JS -> NaCl will arrive containing the pixel data. See + // HandleMessage() method in this file. + pp::VarDictionary message; + message.Set("message", "request_textures"); + pp::VarArray names; + names.Set(0, "earth.jpg"); + names.Set(1, "earthnight.jpg"); + message.Set("names", names); + PostMessage(message); + return true; +} + +void Planet::Reset() { + // Reset has to first fill in all variables with valid floats, so + // CacheCalcs() doesn't potentially propagate NaNs when calling Set*() + // functions further below. + planet_radius_ = 1.0f; + planet_spin_x_ = 0.0f; + planet_spin_y_ = 0.0f; + planet_x_ = 0.0f; + planet_y_ = 0.0f; + planet_z_ = 0.0f; + planet_pole_x_ = 0.0f; + planet_pole_y_ = 0.0f; + planet_pole_z_ = 0.0f; + planet_equator_x_ = 0.0f; + planet_equator_y_ = 0.0f; + planet_equator_z_ = 0.0f; + eye_x_ = 0.0f; + eye_y_ = 0.0f; + eye_z_ = 0.0f; + light_x_ = 0.0f; + light_y_ = 0.0f; + light_z_ = 0.0f; + diffuse_r_ = 0.0f; + diffuse_g_ = 0.0f; + diffuse_b_ = 0.0f; + ambient_r_ = 0.0f; + ambient_g_ = 0.0f; + ambient_b_ = 0.0f; + planet_xyz_ = 0.0f; + planet_pole_x_equator_x_ = 0.0f; + planet_pole_x_equator_y_ = 0.0f; + planet_pole_x_equator_z_ = 0.0f; + planet_radius2_ = 0.0f; + planet_one_over_radius_ = 0.0f; + eye_xyz_ = 0.0f; + ui_zoom_ = 14.0f; + ui_light_ = 1.0f; + ui_spin_x_ = 0.01f; + ui_spin_y_ = 0.0f; + + // Set up reasonable default values. + SetPlanetXYZR(0.0f, 0.0f, 48.0f, 4.0f); + SetEyeXYZ(0.0f, 0.0f, -ui_zoom_); + SetLightXYZ(-60.0f, -30.0f, 0.0f); + SetAmbientRGB(0.05f, 0.05f, 0.05f); + SetDiffuseRGB(0.8f, 0.8f, 0.8f); + SetPlanetPole(0.0f, 1.0f, 0.0f); + SetPlanetEquator(1.0f, 0.0f, 0.0f); + SetPlanetSpin(kPI / 2.0f, kPI / 2.0f); + SetZoom(ui_zoom_); + SetLight(ui_light_); + + // Send UI values to JS to reset html sliders. + PostUpdateMessage("set_zoom", ui_zoom_); + PostUpdateMessage("set_light", ui_light_); +} + + +Planet::Planet(PP_Instance instance) : pp::Instance(instance), + graphics_2d_context_(NULL), + image_data_(NULL), + num_regions_(256) { + width_ = 0; + height_ = 0; + stride_in_pixels_ = 0; + pixel_buffer_ = NULL; + benchmark_frame_counter_ = 0; + benchmarking_ = false; + base_tex_ = NULL; + night_tex_ = NULL; + name_for_tex_ = ""; + + Reset(); + + // By default, render from the dispatch thread. + num_threads_ = 0; + workers_ = new ThreadPool(num_threads_); +} + +Planet::~Planet() { + delete workers_; + DestroyContext(); +} + +// Given a region r, derive a rectangle. +// This rectangle shouldn't overlap with work being done by other workers. +// If multithreading, this function is only called by the worker threads. +void Planet::wMakeRect(int r, int *x, int *y, int *w, int *h) { + int dy = height_ / num_regions_; + *x = 0; + *w = width_; + *y = r * dy; + *h = dy; +} + + +inline uint32_t* Planet::wGetAddr(int x, int y) { + assert(pixel_buffer_); + return (pixel_buffer_ + y * stride_in_pixels_) + x; +} + +// This is the meat of the ray tracer. Given a pixel span (x0, x1) on +// scanline y, shoot rays into the scene and render what they hit. Use +// scanline coherence to do a few optimizations +void Planet::wRenderPixelSpan(int x0, int x1, int y) { + if (!base_tex_ || !night_tex_) + return; + const int kColorBlack = MakeRGBA(0, 0, 0, 0xFF); + float y0 = eye_y_; + float z0 = eye_z_; + float y1 = (static_cast<float>(y) / height_) * 2.0f - 1.0f; + float z1 = 0.0f; + float dy = (y1 - y0); + float dz = (z1 - z0); + float dy_dy_dz_dz = dy * dy + dz * dz; + float two_dy_y0_y_two_dz_z0_z = 2.0f * dy * (y0 - planet_y_) + + 2.0f * dz * (z0 - planet_z_); + float planet_xyz_eye_xyz = planet_xyz_ + eye_xyz_; + float y_y0_z_z0 = planet_y_ * y0 + planet_z_ * z0; + float oowidth = 1.0f / width_; + uint32_t* pixels = this->wGetAddr(x0, y); + for (int x = x0; x <= x1; ++x) { + // scan normalized screen -1..1 + float x1 = (static_cast<float>(x) * oowidth) * 2.0f - 1.0f; + // eye + float x0 = eye_x_; + // delta from screen to eye + float dx = (x1 - x0); + // build a, b, c + float a = dx * dx + dy_dy_dz_dz; + float b = 2.0f * dx * (x0 - planet_x_) + two_dy_y0_y_two_dz_z0_z; + float c = planet_xyz_eye_xyz + + -2.0f * (planet_x_ * x0 + y_y0_z_z0) - (planet_radius2_); + // calculate discriminant + float disc = b * b - 4.0f * a * c; + + // Did ray hit the sphere? + if (disc < 0.0f) { + *pixels = kColorBlack; + ++pixels; + continue; + } + + // calc parametric t value + float t = (-b - inline_sqrt(disc)) / (2.0f * a); + float px = x0 + t * dx; + float py = y0 + t * dy; + float pz = z0 + t * dz; + float nx = (px - planet_x_) * planet_one_over_radius_; + float ny = (py - planet_y_) * planet_one_over_radius_; + float nz = (pz - planet_z_) * planet_one_over_radius_; + + // Misc raytrace calculations. + float Lx = (light_x_ - px); + float Ly = (light_y_ - py); + float Lz = (light_z_ - pz); + float Lq = 1.0f / inline_quick_sqrt(Lx * Lx + Ly * Ly + Lz * Lz); + Lx *= Lq; + Ly *= Lq; + Lz *= Lq; + float d = (Lx * nx + Ly * ny + Lz * nz); + float pr = (diffuse_r_ * d) + ambient_r_; + float pg = (diffuse_g_ * d) + ambient_g_; + float pb = (diffuse_b_ * d) + ambient_b_; + float ds = -(nx * planet_pole_x_ + + ny * planet_pole_y_ + + nz * planet_pole_z_); + float ang = acos_.TableLerp(ds); + float v = ang * kOneOverPI; + float dp = planet_equator_x_ * nx + + planet_equator_y_ * ny + + planet_equator_z_ * nz; + float w = dp / sin(ang); + if (w > 1.0f) w = 1.0f; + if (w < -1.0f) w = -1.0f; + float th = acos_.TableLerp(w) * kOneOver2PI; + float dps = planet_pole_x_equator_x_ * nx + + planet_pole_x_equator_y_ * ny + + planet_pole_x_equator_z_ * nz; + float u; + if (dps < 0.0f) + u = th; + else + u = 1.0f - th; + + // Look up daylight texel. + int tx = static_cast<int>(u * base_tex_->width); + int ty = static_cast<int>(v * base_tex_->height); + int offset = tx + ty * base_tex_->width; + uint32_t base_texel = base_tex_->pixels[offset]; + float tr = ExtractR(base_texel); + float tg = ExtractG(base_texel); + float tb = ExtractB(base_texel); + + float ipr = 1.0f - pr; + if (ipr < 0.0f) ipr = 0.0f; + float ipg = 1.0f - pg; + if (ipg < 0.0f) ipg = 0.0f; + float ipb = 1.0f - pb; + if (ipb < 0.0f) ipb = 0.0f; + + // Look up night texel. + int nix = static_cast<int>(u * night_tex_->width); + int niy = static_cast<int>(v * night_tex_->height); + int noffset = nix + niy * night_tex_->width; + uint32_t night_texel = night_tex_->pixels[noffset]; + float nr = ExtractR(night_texel); + float ng = ExtractG(night_texel); + float nb = ExtractB(night_texel); + + // Final color value is lerp between day and night texels. + unsigned int ir = Clamp255(pr * tr + nr * ipr); + unsigned int ig = Clamp255(pg * tg + ng * ipg); + unsigned int ib = Clamp255(pb * tb + nb * ipb); + + unsigned int color = MakeRGBA(ir, ig, ib, 0xFF); + + *pixels = color; + ++pixels; + } +} + +// Renders a rectangular area of the screen, scan line at a time +void Planet::wRenderRect(int x, int y, int w, int h) { + for (int j = y; j < (y + h); ++j) { + this->wRenderPixelSpan(x, x + w - 1, j); + } +} + +// If multithreading, this function is only called by the worker threads. +void Planet::wRenderRegion(int region) { + // convert region # into x0, y0, x1, y1 rectangle + int x, y, w, h; + wMakeRect(region, &x, &y, &w, &h); + // render this rectangle + wRenderRect(x, y, w, h); +} + +// Entry point for worker thread. Can't pass a member function around, so we +// have to do this little round-about. +void Planet::wRenderRegionEntry(int region, void* thiz) { + static_cast<Planet*>(thiz)->wRenderRegion(region); +} + +// Renders the planet, dispatching the work to multiple threads. +// Note: This Dispatch() is from the main PPAPI thread, so care must be taken +// not to attempt PPAPI calls from the worker threads, since Dispatch() will +// block here until all work is complete. The worker threads are compute only +// and do not make any PPAPI calls. +void Planet::Render() { + workers_->Dispatch(num_regions_, wRenderRegionEntry, this); +} + +// Pre-calculations to make inner loops faster. +void Planet::CacheCalcs() { + planet_xyz_ = planet_x_ * planet_x_ + + planet_y_ * planet_y_ + + planet_z_ * planet_z_; + planet_radius2_ = planet_radius_ * planet_radius_; + planet_one_over_radius_ = 1.0f / planet_radius_; + eye_xyz_ = eye_x_ * eye_x_ + eye_y_ * eye_y_ + eye_z_ * eye_z_; + // spin vector from center->equator + planet_equator_x_ = cos(planet_spin_x_); + planet_equator_y_ = 0.0f; + planet_equator_z_ = sin(planet_spin_x_); + + // cache cross product of pole & equator + planet_pole_x_equator_x_ = planet_pole_y_ * planet_equator_z_ - + planet_pole_z_ * planet_equator_y_; + planet_pole_x_equator_y_ = planet_pole_z_ * planet_equator_x_ - + planet_pole_x_ * planet_equator_z_; + planet_pole_x_equator_z_ = planet_pole_x_ * planet_equator_y_ - + planet_pole_y_ * planet_equator_x_; +} + +void Planet::SetPlanetXYZR(float x, float y, float z, float r) { + planet_x_ = x; + planet_y_ = y; + planet_z_ = z; + planet_radius_ = r; + CacheCalcs(); +} + +void Planet::SetEyeXYZ(float x, float y, float z) { + eye_x_ = x; + eye_y_ = y; + eye_z_ = z; + CacheCalcs(); +} + +void Planet::SetLightXYZ(float x, float y, float z) { + light_x_ = x; + light_y_ = y; + light_z_ = z; + CacheCalcs(); +} + +void Planet::SetAmbientRGB(float r, float g, float b) { + ambient_r_ = r; + ambient_g_ = g; + ambient_b_ = b; + CacheCalcs(); +} + +void Planet::SetDiffuseRGB(float r, float g, float b) { + diffuse_r_ = r; + diffuse_g_ = g; + diffuse_b_ = b; + CacheCalcs(); +} + +void Planet::SetPlanetPole(float x, float y, float z) { + planet_pole_x_ = x; + planet_pole_y_ = y; + planet_pole_z_ = z; + CacheCalcs(); +} + +void Planet::SetPlanetEquator(float x, float y, float z) { + // This is really over-ridden by spin at the momenent. + planet_equator_x_ = x; + planet_equator_y_ = y; + planet_equator_z_ = z; + CacheCalcs(); +} + +void Planet::SetPlanetSpin(float x, float y) { + planet_spin_x_ = x; + planet_spin_y_ = y; + CacheCalcs(); +} + +// Run a simple sim to spin the planet. Update loop is run once per frame. +// Called from the main thread only and only when the worker threads are idle. +void Planet::UpdateSim() { + float x = planet_spin_x_ + ui_spin_x_; + float y = planet_spin_y_ + ui_spin_y_; + // keep in nice range + if (x > (kPI * 2.0f)) + x = x - kPI * 2.0f; + else if (x < (-kPI * 2.0f)) + x = x + kPI * 2.0f; + if (y > (kPI * 2.0f)) + y = y - kPI * 2.0f; + else if (y < (-kPI * 2.0f)) + y = y + kPI * 2.0f; + SetPlanetSpin(x, y); +} + +void Planet::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()); + Update(); +} + +void Planet::StartBenchmark() { + // For more consistent benchmark numbers, reset to default state. + Reset(); + printf("Benchmark started...\n"); + benchmark_frame_counter_ = kFramesToBenchmark; + benchmarking_ = true; + benchmark_start_time_ = getseconds(); +} + +void Planet::EndBenchmark() { + benchmark_end_time_ = getseconds(); + printf("Benchmark ended... time: %2.5f\n", + benchmark_end_time_ - benchmark_start_time_); + benchmarking_ = false; + benchmark_frame_counter_ = 0; + double total_time = benchmark_end_time_ - benchmark_start_time_; + // Send benchmark result to JS. + PostUpdateMessage("benchmark_result", total_time); +} + +void Planet::SetZoom(float zoom) { + ui_zoom_ = std::min(kZoomMax, std::max(kZoomMin, zoom)); + SetEyeXYZ(0.0f, 0.0f, -ui_zoom_); +} + +void Planet::SetLight(float light) { + ui_light_ = std::min(kLightMax, std::max(kLightMin, light)); + SetDiffuseRGB(0.8f * ui_light_, 0.8f * ui_light_, 0.8f * ui_light_); + SetAmbientRGB(0.4f * ui_light_, 0.4f * ui_light_, 0.4f * ui_light_); +} + +void Planet::SetTexture(const std::string& name, int width, int height, + uint32_t* pixels) { + if (pixels) { + if (name == "earth.jpg") { + delete base_tex_; + base_tex_ = new Texture(width, height, pixels); + } else if (name == "earthnight.jpg") { + delete night_tex_; + night_tex_ = new Texture(width, height, pixels); + } + } +} + +// Handle input events from the user. +bool Planet::HandleInputEvent(const pp::InputEvent& event) { + switch (event.GetType()) { + case PP_INPUTEVENT_TYPE_KEYDOWN: { + pp::KeyboardInputEvent key(event); + uint32_t key_code = key.GetKeyCode(); + if (key_code == 84) // 't' key + if (!benchmarking_) + StartBenchmark(); + break; + } + case PP_INPUTEVENT_TYPE_MOUSEMOVE: + case PP_INPUTEVENT_TYPE_MOUSEDOWN: { + pp::MouseInputEvent mouse = pp::MouseInputEvent(event); + if (mouse.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { + PP_Point delta = mouse.GetMovement(); + float delta_x = static_cast<float>(delta.x); + float delta_y = static_cast<float>(delta.y); + float spin_x = std::min(4.0f, std::max(-4.0f, delta_x * 0.5f)); + float spin_y = std::min(4.0f, std::max(-4.0f, delta_y * 0.5f)); + ui_spin_x_ = spin_x / 100.0f; + ui_spin_y_ = spin_y / 100.0f; + } + break; + } + case PP_INPUTEVENT_TYPE_WHEEL: { + pp::WheelInputEvent wheel = pp::WheelInputEvent(event); + PP_FloatPoint ticks = wheel.GetTicks(); + SetZoom(ui_zoom_ + (ticks.x + ticks.y) * kWheelSpeed); + // Update html slider by sending update message to JS. + PostUpdateMessage("set_zoom", ui_zoom_); + break; + } + default: + return false; + } + return true; +} + +// PostUpdateMessage() helper function for sending small messages to JS. +void Planet::PostUpdateMessage(const char* message_name, double value) { + pp::VarDictionary message; + message.Set("message", message_name); + message.Set("value", value); + PostMessage(message); +} + +// Handle message sent from Javascript. +void Planet::HandleMessage(const pp::Var& var) { + if (var.is_dictionary()) { + pp::VarDictionary dictionary(var); + std::string message = dictionary.Get("message").AsString(); + if (message == "run benchmark" && !benchmarking_) { + StartBenchmark(); + } else if (message == "set_light") { + SetLight(static_cast<float>(dictionary.Get("value").AsDouble())); + } else if (message == "set_zoom") { + SetZoom(static_cast<float>(dictionary.Get("value").AsDouble())); + } else if (message == "set_threads") { + int threads = dictionary.Get("value").AsInt(); + delete workers_; + workers_ = new ThreadPool(threads); + } else if (message == "texture") { + std::string name = dictionary.Get("name").AsString(); + int width = dictionary.Get("width").AsInt(); + int height = dictionary.Get("height").AsInt(); + pp::VarArrayBuffer array_buffer(dictionary.Get("data")); + if (!name.empty() && width > 0 && height > 0 && !array_buffer.is_null()) { + uint32_t* pixels = static_cast<uint32_t*>(array_buffer.Map()); + SetTexture(name, width, height, pixels); + array_buffer.Unmap(); + } + } + } else { + printf("Handle message unknown type: %s\n", var.DebugString().c_str()); + } +} + +void Planet::FlushCallback(void* thiz, int32_t result) { + static_cast<Planet*>(thiz)->Update(); +} + +// Update the 2d region and flush to make it visible on the page. +void Planet::FlushPixelBuffer() { + graphics_2d_context_->PaintImageData(*image_data_, pp::Point(0, 0)); + graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this)); +} + +void Planet::Update() { + // Don't call FlushPixelBuffer() when benchmarking - vsync is enabled by + // default, and will throttle the benchmark results. + do { + UpdateSim(); + Render(); + if (!benchmarking_) break; + --benchmark_frame_counter_; + } while (benchmark_frame_counter_ > 0); + if (benchmarking_) + EndBenchmark(); + + FlushPixelBuffer(); +} + +void Planet::CreateContext(const pp::Size& size) { + graphics_2d_context_ = new pp::Graphics2D(this, size, false); + if (graphics_2d_context_->is_null()) + printf("Failed to create a 2D resource!\n"); + if (!BindGraphics(*graphics_2d_context_)) + printf("Couldn't bind the device context\n"); + image_data_ = new pp::ImageData(this, + PP_IMAGEDATAFORMAT_BGRA_PREMUL, + size, + false); + width_ = image_data_->size().width(); + height_ = image_data_->size().height(); + stride_in_pixels_ = static_cast<uint32_t>(image_data_->stride() / 4); + pixel_buffer_ = static_cast<uint32_t*>(image_data_->data()); + num_regions_ = height_; +} + +void Planet::DestroyContext() { + delete graphics_2d_context_; + delete image_data_; + graphics_2d_context_ = NULL; + image_data_ = NULL; + width_ = 0; + height_ = 0; + stride_in_pixels_ = 0; + pixel_buffer_ = NULL; +} + +class PlanetModule : public pp::Module { + public: + PlanetModule() : pp::Module() {} + virtual ~PlanetModule() {} + + // Create and return a Planet instance. + virtual pp::Instance* CreateInstance(PP_Instance instance) { + return new Planet(instance); + } +}; + +namespace pp { +Module* CreateModule() { + return new PlanetModule(); +} +} // namespace pp + diff --git a/native_client_sdk/src/examples/demo/earth/earth.jpg b/native_client_sdk/src/examples/demo/earth/earth.jpg Binary files differnew file mode 100644 index 0000000000..82bd420a87 --- /dev/null +++ b/native_client_sdk/src/examples/demo/earth/earth.jpg diff --git a/native_client_sdk/src/examples/demo/earth/earthnight.jpg b/native_client_sdk/src/examples/demo/earth/earthnight.jpg Binary files differnew file mode 100644 index 0000000000..301f152567 --- /dev/null +++ b/native_client_sdk/src/examples/demo/earth/earthnight.jpg diff --git a/native_client_sdk/src/examples/demo/earth/example.dsc b/native_client_sdk/src/examples/demo/earth/example.dsc new file mode 100644 index 0000000000..21eb60c354 --- /dev/null +++ b/native_client_sdk/src/examples/demo/earth/example.dsc @@ -0,0 +1,22 @@ +{ + 'TOOLS': ['newlib', 'glibc', 'pnacl'], + 'TARGETS': [ + { + 'NAME' : 'earth', + 'TYPE' : 'main', + 'SOURCES' : [ + 'earth.cc' + ], + 'LIBS': ['sdk_util', 'ppapi_cpp', 'ppapi', 'pthread'] + } + ], + 'DATA': [ + 'earth.jpg', + 'earthnight.jpg', + 'example.js', + ], + 'DEST': 'examples/demo', + 'NAME': 'earth', + 'TITLE': 'Multi-Threaded Earth Demo', + 'GROUP': 'Demo' +} diff --git a/native_client_sdk/src/examples/demo/earth/example.js b/native_client_sdk/src/examples/demo/earth/example.js new file mode 100644 index 0000000000..34f6c1142c --- /dev/null +++ b/native_client_sdk/src/examples/demo/earth/example.js @@ -0,0 +1,88 @@ +// Copyright (c) 2013 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. + +function moduleDidLoad() { +} + +function postThreadFunc(numThreads) { + return function () { + common.naclModule.postMessage({'message' : 'set_threads', + 'value' : numThreads}); + } +} + +// Add event listeners after the NaCl module has loaded. These listeners will +// forward messages to the NaCl module via postMessage() +function attachListeners() { + document.getElementById('benchmark').addEventListener('click', + function() { + common.naclModule.postMessage({'message' : 'run benchmark'}); + common.updateStatus('BENCHMARKING... (please wait)'); + }); + var threads = [0, 1, 2, 4, 6, 8, 12, 16, 24, 32]; + for (var i = 0; i < threads.length; i++) { + document.getElementById('radio'+i).addEventListener('click', + postThreadFunc(threads[i])); + } + document.getElementById('zoomRange').addEventListener('change', + function() { + var value = parseFloat(document.getElementById('zoomRange').value); + common.naclModule.postMessage({'message' : 'set_zoom', + 'value' : value}); + }); + document.getElementById('lightRange').addEventListener('change', + function() { + var value = parseFloat(document.getElementById('lightRange').value); + common.naclModule.postMessage({'message' : 'set_light', + 'value' : value}); + }); +} + +// Load a texture and send pixel data down to NaCl module. +function loadTexture(name) { + // Load image from jpg, decompress into canvas. + var img = new Image(); + img.onload = function() { + var graph = document.createElement('canvas'); + graph.width = img.width; + graph.height = img.height; + var context = graph.getContext('2d'); + context.drawImage(img, 0, 0); + var imageData = context.getImageData(0, 0, img.width, img.height); + // Send NaCl module the raw image data obtained from canvas. + common.naclModule.postMessage({'message' : 'texture', + 'name' : name, + 'width' : img.width, + 'height' : img.height, + 'data' : imageData.data.buffer}); + } + img.src = name; +} + +// Handle a message coming from the NaCl module. +function handleMessage(message_event) { + if (message_event.data['message'] == 'benchmark_result') { + // benchmark result + var result = message_event.data['value']; + console.log('Benchmark result:' + result); + result = (Math.round(result * 1000) / 1000).toFixed(3); + document.getElementById('result').textContent = + 'Result: ' + result + ' seconds'; + common.updateStatus('SUCCESS'); + } else if (message_event.data['message'] == 'set_zoom') { + // zoom slider + var zoom = message_event.data['value']; + document.getElementById('zoomRange').value = zoom; + } else if (message_event.data['message'] == 'set_light') { + // light slider + var light = message_event.data['value']; + document.getElementById('lightRange').value = light; + } else if (message_event.data['message'] == 'request_textures') { + // NaCl module is requesting a set of textures. + var names = message_event.data['names']; + for (var i = 0; i < names.length; i++) + loadTexture(names[i]); + } +} + diff --git a/native_client_sdk/src/examples/demo/earth/index.html b/native_client_sdk/src/examples/demo/earth/index.html new file mode 100644 index 0000000000..8ca4aa42aa --- /dev/null +++ b/native_client_sdk/src/examples/demo/earth/index.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<html> +<!-- +Copyright (c) 2013 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> + <meta http-equiv="Pragma" content="no-cache"> + <meta http-equiv="Expires" content="-1"> + <meta charset="UTF-8"> + <title>{{title}}</title> + <script type="text/javascript" src="common.js"></script> + <script type="text/javascript" src="example.js"></script> +</head> +<body {{attrs}} data-width="640" data-height="640"> + <h1>{{title}}</h1> + <h2>Status: <code id="statusField">NO-STATUS</code></h2> + <div> + This demo renders a rotating globe using the Graphics2D interface. + Image Credit: + NASA Goddard Space Flight Center Image by Reto Stöckli (land surface, + shallow water, clouds). Enhancements by Robert Simmon (ocean color, + compositing, 3D globes, animation). + Data and technical support: MODIS Land Group; MODIS Science Data, + Support Team; MODIS Atmosphere Group; MODIS Ocean Group Additional data: + USGS EROS Data Center (topography); USGS Terrestrial Remote Sensing + Flagstaff Field Center (Antarctica); Defense Meteorological + Satellite Program (city lights). + <br> + Zoom: + <input type="range" id="zoomRange" + min="1.0" max="50.0" step="0.5" value="14.0" /> + Light: + <input type="range" id="lightRange" + min="0.2" max="2.0" step=".01" value="1.0" /> + <br> + Number of threads (0 is main thread): + <input type="radio" name="threadCount" id="radio0" value="0" + checked="checked"> + <label for="radio0">0</label> + <input type="radio" name="threadCount" id="radio1" value="1"> + <label for="radio1">1</label> + <input type="radio" name="threadCount" id="radio2" value="2"> + <label for="radio2">2</label> + <input type="radio" name="threadCount" id="radio3" value="4"> + <label for="radio3">4</label> + <input type="radio" name="threadCount" id="radio4" value="6"> + <label for="radio4">6</label> + <input type="radio" name="threadCount" id="radio5" value="8"> + <label for="radio5">8</label> + <input type="radio" name="threadCount" id="radio6" value="12"> + <label for="radio6">12</label> + <input type="radio" name="threadCount" id="radio7" value="16"> + <label for="radio7">16</label> + <input type="radio" name="threadCount" id="radio8" value="24"> + <label for="radio8">24</label> + <input type="radio" name="threadCount" id="radio9" value="32"> + <label for="radio9">32</label> + <br> + <input type="submit" id="benchmark" value="Run Benchmark"> + <label id="result" name="result"> </label> + </div> + <!-- The NaCl plugin will be embedded inside the element with id "listener". + See common.js.--> + <div id="listener"></div> +</body> +</html> diff --git a/native_client_sdk/src/examples/demo/flock/example.dsc b/native_client_sdk/src/examples/demo/flock/example.dsc new file mode 100644 index 0000000000..50fdeeb462 --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/example.dsc @@ -0,0 +1,26 @@ +{ + 'TOOLS': ['newlib', 'glibc', 'pnacl'], + 'TARGETS': [ + { + 'NAME' : 'flock', + 'TYPE' : 'main', + 'SOURCES' : [ + 'flock.cc', + 'goose.cc', + 'goose.h', + 'sprite.cc', + 'sprite.h', + 'vector2.h' + ], + 'DEPS': ['ppapi_simple', 'nacl_io'], + 'LIBS': ['ppapi_cpp', 'ppapi', 'pthread'] + } + ], + 'DATA': [ + 'images/flock_green.raw' + ], + 'DEST': 'examples/demo', + 'NAME': 'flock', + 'TITLE': 'Flocking Geese', + 'GROUP': 'Demo' +} diff --git a/native_client_sdk/src/examples/demo/flock/flock.cc b/native_client_sdk/src/examples/demo/flock/flock.cc new file mode 100644 index 0000000000..de23691149 --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/flock.cc @@ -0,0 +1,150 @@ +// Copyright 2013 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. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <algorithm> +#include <sstream> +#include <string> +#include <vector> + +#include "nacl_io/nacl_io.h" + +#include "ppapi/c/pp_rect.h" +#include "ppapi/c/pp_size.h" + +#include "ppapi_simple/ps_context_2d.h" +#include "ppapi_simple/ps_main.h" + +#include "goose.h" +#include "sprite.h" +#include "vector2.h" + + +namespace { + // The goose sprites rotate in increments of 5 degrees. +const double kGooseHeadingIncrement = (5.0 * M_PI) / 180.0; +} // namespace + +struct ImageFormat { + int width; + int height; + int channels; +}; + +Sprite* g_goose_sprite; +std::vector<Goose> g_geese; +std::vector<Vector2> g_attractors; + + +void ResetFlock(PSContext2D_t* ctx, size_t count) { + Vector2 center(0.5 * ctx->width, 0.5 * ctx->height); + + g_geese.resize(count); + for (size_t i = 0; i < count; i++) { + double dx = (double) rand() / (double) RAND_MAX; + double dy = (double) rand() / (double) RAND_MAX; + g_geese[i] = Goose(center, Vector2(dx, dy)); + } +} + +void Render(PSContext2D_t* ctx) { + PSContext2DGetBuffer(ctx); + const size_t num_geese = g_geese.size(); + + if (NULL == g_goose_sprite) return; + if (NULL == ctx->data) return; + + // Clear to WHITE + memset(ctx->data, 0xFF, ctx->stride * ctx->height); + + int32_t sprite_side_length = g_goose_sprite->size().height(); + pp::Rect sprite_src_rect(0, 0, sprite_side_length, sprite_side_length); + pp::Rect canvas_bounds(pp::Size(ctx->width, ctx->height)); + + + // Run the simulation for each goose. + for (size_t i = 0; i < num_geese; i++) { + Goose& goose = g_geese[i]; + + // Update position and orientation + goose.SimulationTick(g_geese, g_attractors, canvas_bounds); + pp::Point dest_point(goose.location().x() - sprite_side_length / 2, + goose.location().y() - sprite_side_length / 2); + + // Compute image to use + double heading = goose.velocity().Heading(); + if (heading < 0.0) + heading = M_PI * 2.0 + heading; + + int32_t sprite_index = + static_cast<int32_t>(heading / kGooseHeadingIncrement); + + sprite_src_rect.set_x(sprite_index * sprite_side_length); + g_goose_sprite->CompositeFromRectToPoint( + sprite_src_rect, + ctx->data, canvas_bounds, 0, + dest_point); + } + + PSContext2DSwapBuffer(ctx); +} + +/* + * Starting point for the module. We do not use main since it would + * collide with main in libppapi_cpp. + */ +int example_main(int argc, char *argv[]) { + ImageFormat fmt; + uint32_t* buffer; + size_t len; + + PSEventSetFilter(PSE_ALL); + + // Mount the images directory as an HTTP resource. + mount("images", "/images", "httpfs", 0, ""); + + FILE* fp = fopen("/images/flock_green.raw", "rb"); + fread(&fmt, sizeof(fmt), 1, fp); + + len = fmt.width * fmt.height * fmt.channels; + buffer = new uint32_t[len]; + fread(buffer, 1, len, fp); + fclose(fp); + + g_goose_sprite = new Sprite(buffer, pp::Size(fmt.width, fmt.height), 0); + + PSContext2D_t* ctx = PSContext2DAllocate(); + ResetFlock(ctx, 50); + while (1) { + PSEvent* event; + + // Consume all available events + while ((event = PSEventTryAcquire()) != NULL) { + PSContext2DHandleEvent(ctx, event); + PSEventRelease(event); + } + + if (ctx->bound) { + Render(ctx); + } else { + // If not bound, wait for an event which may signal a context becoming + // available, instead of spinning. + event = PSEventWaitAcquire(); + if (PSContext2DHandleEvent(ctx, event)) + ResetFlock(ctx, 50); + PSEventRelease(event); + } + } + + return 0; +} + +/* + * Register the function to call once the Instance Object is initialized. + * see: pappi_simple/ps_main.h + */ +PPAPI_SIMPLE_REGISTER_MAIN(example_main); diff --git a/native_client_sdk/src/examples/demo/flock/frame_counter.cc b/native_client_sdk/src/examples/demo/flock/frame_counter.cc new file mode 100644 index 0000000000..e9e4c54534 --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/frame_counter.cc @@ -0,0 +1,43 @@ +// Copyright 2013 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. + +#include <time.h> +#include <limits> + +#include "nacl_app/flock.h" + +void FrameCounter::BeginFrame() { + struct timeval start_time; + gettimeofday(&start_time, NULL); + frame_start_ = start_time.tv_sec * kMicroSecondsPerSecond + + start_time.tv_usec; +} + +void FrameCounter::EndFrame() { + struct timeval end_time; + gettimeofday(&end_time, NULL); + double frame_end = end_time.tv_sec * kMicroSecondsPerSecond + + end_time.tv_usec; + double dt = frame_end - frame_start_; + if (dt < 0) + return; + frame_duration_accumulator_ += dt; + frame_count_++; + if (frame_count_ > kFrameRateRefreshCount || + frame_duration_accumulator_ >= kMicroSecondsPerSecond) { + double elapsed_time = frame_duration_accumulator_ / + kMicroSecondsPerSecond; + if (fabs(elapsed_time) > std::numeric_limits<double>::epsilon()) { + frames_per_second_ = frame_count_ / elapsed_time; + } + frame_duration_accumulator_ = 0; + frame_count_ = 0; + } +} + +void FrameCounter::Reset() { + frames_per_second_ = 0; + frame_duration_accumulator_ = 0; + frame_count_ = 0; +} diff --git a/native_client_sdk/src/examples/demo/flock/frame_counter.h b/native_client_sdk/src/examples/demo/flock/frame_counter.h new file mode 100644 index 0000000000..26a1b66288 --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/frame_counter.h @@ -0,0 +1,46 @@ +// Copyright 2013 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. + +#ifndef FRAME_COUNTER_H_ +#define FRAME_COUNTER_H_ + +class FrameCounter { + public: + FrameCounter() + : frame_duration_accumulator_(0), + frame_count_(0), + frames_per_second_(0) {} + ~FrameCounter() {} + + // Record the current time, which is used to compute the frame duration + // when EndFrame() is called. + void BeginFrame(); + + // Compute the delta since the last call to BeginFrame() and increment the + // frame count. Update the frame rate whenever the prescribed number of + // frames have been counted, or at least one second of simulator time has + // passed, whichever is less. + void EndFrame(); + + // Reset the frame counters back to 0. + void Reset(); + + // The current frame rate. Note that this is 0 for the first second in + // the accumulator, and is updated every 100 frames (and at least once + // every second of simulation time or so). + double frames_per_second() const { + return frames_per_second_; + } + + private: + static const double kMicroSecondsPerSecond = 1000000.0; + static const int32_t kFrameRateRefreshCount = 100; + + double frame_duration_accumulator_; // Measured in microseconds. + int32_t frame_count_; + double frame_start_; + double frames_per_second_; +}; + +#endif // FRAME_COUNTER_H_ diff --git a/native_client_sdk/src/examples/demo/flock/goose.cc b/native_client_sdk/src/examples/demo/flock/goose.cc new file mode 100644 index 0000000000..f706134c65 --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/goose.cc @@ -0,0 +1,204 @@ + // Copyright 2013 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. + +#include "goose.h" + +namespace { +// The maximum speed of a goose. Measured in meters/second. +const double kMaxSpeed = 2.0; + +// The maximum force that can be applied to turn a goose when computing the +// aligment. Measured in meters/second/second. +const double kMaxTurningForce = 0.05; + +// The neighbour radius of a goose. Only geese within this radius will affect +// the flocking computations of this goose. Measured in pixels. +const double kNeighbourRadius = 64.0; + +// The minimum distance that a goose can be from this goose. If another goose +// comes within this distance of this goose, the flocking algorithm tries to +// move the geese apart. Measured in pixels. +const double kPersonalSpace = 32.0; + +// The distance at which attractors have effect on a goose's direction. +const double kAttractorRadius = 320.0; + +// The goose will try to turn towards geese within this distance (computed +// during the cohesion phase). Measured in pixels. +const double kMaxTurningDistance = 100.0; + +// The weights used when computing the weighted sum the three flocking +// components. +const double kSeparationWeight = 2.0; +const double kAlignmentWeight = 1.0; +const double kCohesionWeight = 1.0; + +} // namespace + + +Goose::Goose() : location_(0, 0), velocity_(0, 0) { +} + +Goose::Goose(const Vector2& location, const Vector2& velocity) + : location_(location), + velocity_(velocity) { +} + +void Goose::SimulationTick(const std::vector<Goose>& geese, + const std::vector<Vector2>& attractors, + const pp::Rect& flock_box) { + + Vector2 acceleration = DesiredVector(geese, attractors); + velocity_.Add(acceleration); + + // Limit the velocity to a maximum speed. + velocity_.Clamp(kMaxSpeed); + + location_.Add(velocity_); + + // Wrap the goose location to the flock box. + if (!flock_box.IsEmpty()) { + while (location_.x() < flock_box.x()) + location_.set_x(location_.x() + flock_box.width()); + + while (location_.x() >= flock_box.right()) + location_.set_x(location_.x() - flock_box.width()); + + while (location_.y() < flock_box.y()) + location_.set_y(location_.y() + flock_box.height()); + + while (location_.y() >= flock_box.bottom()) + location_.set_y(location_.y() - flock_box.height()); + } +} + +Vector2 Goose::DesiredVector(const std::vector<Goose>& geese, + const std::vector<Vector2>& attractors) { + // Loop over all the neighbouring geese in the flock, accumulating + // the separation mean, the alignment mean and the cohesion mean. + int32_t separation_count = 0; + Vector2 separation; + int32_t align_count = 0; + Vector2 alignment; + int32_t cohesion_count = 0; + Vector2 cohesion; + + for (std::vector<Goose>::const_iterator goose_it = geese.begin(); + goose_it < geese.end(); + ++goose_it) { + const Goose& goose = *goose_it; + + // Compute the distance from this goose to its neighbour. + Vector2 goose_delta = Vector2::Difference( + location_, goose.location()); + double distance = goose_delta.Magnitude(); + + separation_count = AccumulateSeparation( + distance, goose_delta, &separation, separation_count); + + align_count = AccumulateAlignment( + distance, goose, &alignment, align_count); + cohesion_count = AccumulateCohesion( + distance, goose, &cohesion, cohesion_count); + } + + // Compute the means and create a weighted sum. This becomes the goose's new + // acceleration. + if (separation_count > 0) { + separation.Scale(1.0 / static_cast<double>(separation_count)); + } + if (align_count > 0) { + alignment.Scale(1.0 / static_cast<double>(align_count)); + // Limit the effect that alignment has on the final acceleration. The + // alignment component can overpower the others if there is a big + // difference between this goose's velocity and its neighbours'. + alignment.Clamp(kMaxTurningForce); + } + + // Compute the effect of the attractors and blend this in with the flock + // cohesion component. An attractor has to be within kAttractorRadius to + // effect the heading of a goose. + for (size_t i = 0; i < attractors.size(); ++i) { + Vector2 attractor_direction = Vector2::Difference( + attractors[i], location_); + double distance = attractor_direction.Magnitude(); + if (distance < kAttractorRadius) { + attractor_direction.Scale(1000); // Each attractor acts like 1000 geese. + cohesion.Add(attractor_direction); + cohesion_count++; + } + } + + // If there is a non-0 cohesion component, steer the goose so that it tries + // to follow the flock. + if (cohesion_count > 0) { + cohesion.Scale(1.0 / static_cast<double>(cohesion_count)); + cohesion = TurnTowardsTarget(cohesion); + } + // Compute the weighted sum. + separation.Scale(kSeparationWeight); + alignment.Scale(kAlignmentWeight); + cohesion.Scale(kCohesionWeight); + Vector2 weighted_sum = cohesion; + weighted_sum.Add(alignment); + weighted_sum.Add(separation); + return weighted_sum; +} + +Vector2 Goose::TurnTowardsTarget(const Vector2& target) { + Vector2 desired_direction = Vector2::Difference(target, location_); + double distance = desired_direction.Magnitude(); + Vector2 new_direction; + if (distance > 0.0) { + desired_direction.Normalize(); + // If the target is within the turning affinity distance, then make the + // desired direction based on distance to the target. Otherwise, base + // the desired direction on MAX_SPEED. + if (distance < kMaxTurningDistance) { + // Some pretty arbitrary dampening. + desired_direction.Scale(kMaxSpeed * distance / 100.0); + } else { + desired_direction.Scale(kMaxSpeed); + } + new_direction = Vector2::Difference(desired_direction, velocity_); + new_direction.Clamp(kMaxTurningForce); + } + return new_direction; +} + +int32_t Goose::AccumulateSeparation(double distance, + const Vector2& goose_delta, + Vector2* separation, /* inout */ + int32_t separation_count) { + if (distance > 0.0 && distance < kPersonalSpace) { + Vector2 weighted_direction = goose_delta; + weighted_direction.Normalize(); + weighted_direction.Scale(1.0 / distance); + separation->Add(weighted_direction); + separation_count++; + } + return separation_count; +} + +int32_t Goose::AccumulateAlignment(double distance, + const Goose& goose, + Vector2* alignment, /* inout */ + int32_t align_count) { + if (distance > 0.0 && distance < kNeighbourRadius) { + alignment->Add(goose.velocity()); + align_count++; + } + return align_count; +} + +int32_t Goose::AccumulateCohesion(double distance, + const Goose& goose, + Vector2* cohesion, /* inout */ + int32_t cohesion_count) { + if (distance > 0.0 && distance < kNeighbourRadius) { + cohesion->Add(goose.location()); + cohesion_count++; + } + return cohesion_count; +} diff --git a/native_client_sdk/src/examples/demo/flock/goose.h b/native_client_sdk/src/examples/demo/flock/goose.h new file mode 100644 index 0000000000..2bd5be0518 --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/goose.h @@ -0,0 +1,129 @@ +// Copyright 2013 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. + +#ifndef GOOSE_H_ +#define GOOSE_H_ + +#include <vector> +#include "ppapi/cpp/rect.h" + +#include "vector2.h" + +// A Goose. Each goose has a location and a velocity. Implements the +// flocking algortihm described here: +// http://processingjs.org/learning/topic/flocking with references to +// http://harry.me/2011/02/17/neat-algorithms---flocking. +class Goose { + public: + // Initialize a Goose at location (0, 0) no velocity. + Goose(); + + // Initialize a Goose at the given location with the specified velocity. + Goose(const Vector2& location, const Vector2& velocity); + + // Run one tick of the simulation. Compute a new acceleration based on the + // flocking algorithm (see Goose.flock()) and update the goose's location + // by integrating acceleration and velocity. + // @param geese The list of all the geese in the flock. + // @param attractors The list of attractors. Geese have affinity for these + // points. + // @param flockBox The geese will stay inside of this box. If the flock_box + // is empty, the geese don't have boundaries. + void SimulationTick(const std::vector<Goose>& geese, + const std::vector<Vector2>& attractors, + const pp::Rect& flock_box); + + // Implement the flocking algorithm in five steps: + // 1. Compute the separation component, + // 2. Compute the alignment component, + // 3. Compute the cohesion component. + // 4. Compute the effect of the attractors and blend this in with the + // cohesion component. + // 5. Create a weighted sum of the three components and use this as the + // new acceleration for the goose. + // This is an O(n^2) version of the algorithm. There are ways to speed this + // up using spatial coherence techniques, but this version is much simpler. + // @param geese The list of all the neighbouring geese (in this + // implementation, this is all the geese in the flock). + // @param attractors The list of attractors. Geese have affinity for these + // points. + // @return The acceleration vector for this goose based on the flocking + // algorithm. + Vector2 DesiredVector(const std::vector<Goose>& geese, + const std::vector<Vector2>& attractors); + + // Turn the goose towards a target. The amount of turning force is clamped + // to |kMaxTurningForce|. + // @param target Turn the goose towards this target. + // @return A vector representing the new direction of the goose. + Vector2 TurnTowardsTarget(const Vector2& target); + + // Accessors for location and velocoity. + Vector2 location() const { + return location_; + } + Vector2 velocity() const { + return velocity_; + } + + private: + // Add a neighbouring goose's contribution to the separation mean. Only + // consider geese that have moved inside of this goose's personal space. + // Modifies the separation accumulator |separation| in-place. + // @param distance The distance from this goose to the neighbouring goose. + // @param gooseDirection The direction vector from this goose to the + // neighbour. + // @param separation The accumulated separation from all the neighbouring + // geese. + // @param separationCount The current number of geese that have contributed to + // the separation component so far. + // @return The new count of geese that contribute to the separation component. + // If the goose under consideration does not contribute, this value is the + // same as |separationCount|. + int32_t AccumulateSeparation(double distance, + const Vector2& goose_direction, + Vector2* separation, /* inout */ + int32_t separation_count); + + // Add a neighbouring goose's contribution to the alignment mean. Alignment + // is the average velocity of the neighbours. Only consider geese that are + // within |kNeighbourRadius|. Modifies the alignment accumulator |alignment| + // in-place. + // @param distance The distance from this goose to the neighbouring goose. + // @param goose The neighbouring goose under consideration. + // @param alignment The accumulated alignment from all the neighbouring geese. + // @param alignCount The current number of geese that have contributed to the + // alignment component so far. + // @return The new count of geese that contribute to the alignment component. + // If the goose under consideration does not contribute, this value is the + // same as |alignCount|. + int32_t AccumulateAlignment(double distance, + const Goose& goose, + Vector2* alignment, /* inout */ + int32_t align_count); + + // Add a neighbouring goose's contribution to the cohesion mean. Cohesion is + // based on the average location of the neighbours. The goose attempts to + // point to this average location. Only consider geese that are within + // |kNeighbourRadius|. Modifies the cohesion accumulator |cohesion| in-place. + // @param {!number} distance The distance from this goose to the neighbouring + // goose. + // @param {!Goose} goose The neighbouring goose under consideration. + // @param {!goog.math.Vec2} cohesion The accumulated cohesion from all the + // neighbouring geese. + // @param {!number} cohesionCount The current number of geese that have + // contributed to the cohesion component so far. + // @return {!number} The new count of geese that contribute to the cohesion + // component. If the goose under consideration does not contribute, this + // value is the same as |cohesionCount|. + int32_t AccumulateCohesion(double distance, + const Goose& goose, + Vector2* cohesion, /* inout */ + int32_t cohesion_count); + + Vector2 location_; + Vector2 velocity_; +}; + +#endif // GOOSE_H_ diff --git a/native_client_sdk/src/examples/demo/flock/index.html b/native_client_sdk/src/examples/demo/flock/index.html new file mode 100644 index 0000000000..d20182dbaa --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/index.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<!-- +Copyright 2013 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> + <meta http-equiv="Pragma" content="no-cache"> + <meta http-equiv="Expires" content="-1"> + <title>{{title}}</title> + <script type="text/javascript" src="common.js"></script> +</head> +<body data-width="800" data-height="800" {{attrs}}> + <h1>{{title}}</h1> + <h2>Status: <code id="statusField">NO-STATUS</code></h2> + <p> Simulates the swarming and avoiding flight behavior of a flock of + geese. This demo using the ppapi_simple library to automatically handle + creation and maintainence of the 2D graphics context, providing a pointer + and stride for a 32 bit (RGBA/ARGB) area in which the example draws + every frame. + </p> + <!-- The NaCl plugin will be embedded inside the element with id "listener". + See common.js.--> + <div id="listener"></div> +</body> +</html> diff --git a/native_client_sdk/src/examples/demo/flock/sprite.cc b/native_client_sdk/src/examples/demo/flock/sprite.cc new file mode 100644 index 0000000000..553fc55b83 --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/sprite.cc @@ -0,0 +1,91 @@ +// Copyright 2013 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. + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <algorithm> + +#include "sprite.h" + +namespace { + +inline uint32_t Blend(uint32_t src1, uint32_t src2) { + // Divide both sources by 2, then add them together using a mask + // to avoid overflow. + src1 = (src1 >> 1) & 0x7F7F7F7F; + src2 = (src2 >> 1) & 0x7F7F7F7F; + return src1 + src2; +} + +} // namespace + + +Sprite::Sprite(uint32_t* pixel_buffer, + const pp::Size& size, + int32_t row_bytes) { + SetPixelBuffer(pixel_buffer, size, row_bytes); +} + +Sprite::~Sprite() { + delete[] pixel_buffer_; +} + +void Sprite::SetPixelBuffer(uint32_t* pixel_buffer, + const pp::Size& size, + int32_t row_bytes) { + pixel_buffer_ = pixel_buffer; + pixel_buffer_size_ = size; + row_bytes_ = row_bytes ? row_bytes : size.width() * sizeof(uint32_t); +} + +void Sprite::CompositeFromRectToPoint(const pp::Rect& src_rect, + uint32_t* dest_pixel_buffer, + const pp::Rect& dest_bounds, + int32_t dest_row_bytes, + const pp::Point& dest_point) const { + // Clip the source rect to the source image bounds. + pp::Rect src_bounds(pp::Point(), size()); + pp::Rect src_rect_clipped(src_rect.Intersect(src_bounds)); + if (src_rect_clipped.IsEmpty()) + return; + + // Create a clipped rect in the destination coordinate space that contains the + // final image. + pp::Rect draw_rect(dest_point, src_rect_clipped.size()); + pp::Rect draw_rect_clipped(dest_bounds.Intersect(draw_rect)); + if (draw_rect_clipped.IsEmpty()) + return; + // Transform the dest rect to the source image coordinate system. + pp::Point src_offset(draw_rect_clipped.point()); + src_offset -= dest_point; + src_rect_clipped.Offset(src_offset); + src_rect_clipped.set_size(draw_rect_clipped.size()); + size_t src_byte_offset = src_rect_clipped.x() * sizeof(uint32_t) + + src_rect_clipped.y() * row_bytes_; + const uint8_t* src_pixels = + reinterpret_cast<const uint8_t*>(pixel_buffer_) + src_byte_offset; + + if (dest_row_bytes == 0) + dest_row_bytes = dest_bounds.width() * sizeof(uint32_t); + size_t dest_byte_offset = draw_rect_clipped.point().x() * sizeof(uint32_t) + + draw_rect_clipped.point().y() * dest_row_bytes; + uint8_t* dest_pixels = reinterpret_cast<uint8_t*>(dest_pixel_buffer) + + dest_byte_offset; + + for (int32_t y = 0; y < src_rect_clipped.height(); ++y) { + const uint32_t* src_scanline = + reinterpret_cast<const uint32_t*>(src_pixels); + uint32_t* dest_scanline = reinterpret_cast<uint32_t*>(dest_pixels); + for (int32_t x = 0; x < src_rect_clipped.width(); ++x) { + uint32_t src = *src_scanline++; + uint32_t dst = *dest_scanline; + *dest_scanline++ = Blend(dst, src); + } + src_pixels += row_bytes_; + dest_pixels += dest_row_bytes; + } +} + diff --git a/native_client_sdk/src/examples/demo/flock/sprite.h b/native_client_sdk/src/examples/demo/flock/sprite.h new file mode 100644 index 0000000000..b846768f56 --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/sprite.h @@ -0,0 +1,57 @@ +// Copyright 2013 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. + +#ifndef SPRITE_H_ +#define SPRITE_H_ + +#include <vector> +#include "ppapi/cpp/point.h" +#include "ppapi/cpp/rect.h" +#include "ppapi/cpp/size.h" + + +// A Sprite is a simple container of a pixel buffer. It knows how to +// composite itself to another pixel buffer of the same format. +class Sprite { + public: + // Initialize a Sprite to use the attached pixel buffer. The Sprite takes + // ownership of the pixel buffer, deleting it in the dtor. The pixel + // buffer is assumed to be 32-bit ARGB-8-8-8-8 pixel format, with pre- + // multiplied alpha. If |row_bytes| is 0, then the number of bytes per row + // is assumed to be size.width() * sizeof(uint32_t). + Sprite(uint32_t* pixel_buffer, const pp::Size& size, int32_t stride = 0); + + // Delete the pixel buffer. It is assumed that the pixel buffer was created + // using malloc(). + ~Sprite(); + + // Reset the internal pixel buffer to a new one. Deletes the old pixel + // buffer. Sprite takes ownership of the new pixel buffer. If |row_bytes| + // is 0, then the number of bytes per row is assumed to be size.width() * + // sizeof(uint32_t). + void SetPixelBuffer(uint32_t* pixel_buffer, + const pp::Size& size, + int32_t row_bytes); + + // Composite the section of the Sprite contained in |src_rect| into the given + // pixel buffer at |dest_point|. Performs an average of the source and + // dest pixel, and all necessary clipping. + void CompositeFromRectToPoint(const pp::Rect& src_rect, + uint32_t* dest_pixel_buffer, + const pp::Rect& dest_bounds, + int32_t dest_row_bytes, + const pp::Point& dest_point) const; + + // Accessors. + const pp::Size& size() const { + return pixel_buffer_size_; + } + + private: + uint32_t* pixel_buffer_; + pp::Size pixel_buffer_size_; + int32_t row_bytes_; +}; + +#endif // SPRITE_H_ diff --git a/native_client_sdk/src/examples/demo/flock/vector2.h b/native_client_sdk/src/examples/demo/flock/vector2.h new file mode 100644 index 0000000000..9d68178f6d --- /dev/null +++ b/native_client_sdk/src/examples/demo/flock/vector2.h @@ -0,0 +1,81 @@ +// Copyright 2013 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. + +#ifndef VECTOR2_H_ +#define VECTOR2_H_ + +#include <stdlib.h> +#include <cmath> +#include <limits> + +// A small class that encapsulates a 2D vector. Provides a few simple +// operations. + +class Vector2 { + public: + Vector2() : x_(0.0), y_(0.0) {} + Vector2(double x, double y) : x_(x), y_(y) {} + ~Vector2() {} + + // Create a new vector that represents a - b. + static Vector2 Difference(const Vector2& a, const Vector2& b) { + Vector2 diff(a.x() - b.x(), a.y() - b.y()); + return diff; + } + + // The magnitude of this vector. + double Magnitude() const { + return sqrt(x_ * x_ + y_ * y_); + } + + // Add |vec| to this vector. Works in-place. + void Add(const Vector2& vec) { + x_ += vec.x(); + y_ += vec.y(); + } + + // Normalize this vector in-place. If the vector is degenerate (size 0) + // then do nothing. + void Normalize() { + double mag = Magnitude(); + if (fabs(mag) < std::numeric_limits<double>::epsilon()) + return; + Scale(1.0 / mag); + } + + // Scale the vector in-place by |scale|. + void Scale(double scale) { + x_ *= scale; + y_ *= scale; + } + + // Clamp a vector to a maximum magnitude. Works on the vector in-place. + // @param max_mag The maximum magnitude of the vector. + void Clamp(double max_mag) { + double mag = Magnitude(); + if (mag > max_mag) { + Scale(max_mag / mag); // Does Normalize() followed by Scale(max_mag). + } + } + + // Compute the "heading" of a vector - this is the angle in radians between + // the vector and the x-axis. + // @return {!number} The "heading" angle in radians. + double Heading() const { + double angle = atan2(y_, x_); + return angle; + } + + // Accessors and mutators for the coordinate values. + double x() const { return x_; } + void set_x(double x) { x_ = x; } + + double y() const { return y_; } + void set_y(double y) { y_ = y; } + + double x_; + double y_; +}; + +#endif // VECTOR2_H_ diff --git a/native_client_sdk/src/examples/demo/life/index.html b/native_client_sdk/src/examples/demo/life/index.html index 626f532b5b..6a5978fdd6 100644 --- a/native_client_sdk/src/examples/demo/life/index.html +++ b/native_client_sdk/src/examples/demo/life/index.html @@ -14,8 +14,6 @@ found in the LICENSE file. <body data-width="640" data-height="640" {{attrs}}> <h1>{{title}}</h1> <h2>Status: <code id="statusField">NO-STATUS</code></h2> - <p> - </p> <!-- The NaCl plugin will be embedded inside the element with id "listener". See common.js.--> <div id="listener"></div> diff --git a/native_client_sdk/src/examples/demo/nacl_io/example.js b/native_client_sdk/src/examples/demo/nacl_io/example.js index 5221e5d013..5a75f167b4 100644 --- a/native_client_sdk/src/examples/demo/nacl_io/example.js +++ b/native_client_sdk/src/examples/demo/nacl_io/example.js @@ -8,10 +8,10 @@ function moduleDidLoad() { // Called by the common.js module. function domContentLoaded(name, tc, config, width, height) { - window.webkitStorageInfo.requestQuota(window.PERSISTENT, 1024*1024, + navigator.webkitPersistentStorage.requestQuota(1024 * 1024, function(bytes) { common.updateStatus( - 'Allocated '+bytes+' bytes of persistant storage.'); + 'Allocated ' + bytes + ' bytes of persistant storage.'); common.createNaClModule(name, tc, config, width, height); common.attachDefaultListeners(); }, @@ -78,7 +78,7 @@ function fopen(e) { function fopenResult(filename, filehandle) { filehandle_map[filehandle] = filename; - addFilenameToSelectElements(filehandle, filename) + addFilenameToSelectElements(filehandle, filename); common.logMessage('File ' + filename + ' opened successfully.\n'); } @@ -143,6 +143,7 @@ function statResult(filename, size) { * * @param {string} s The string to search. * @param {string} prefix The prefix to search for in |s|. + * @return {boolean} Whether |s| starts with |prefix|. */ function startsWith(s, prefix) { // indexOf would search the entire string, lastIndexOf(p, 0) only checks at diff --git a/native_client_sdk/src/examples/demo/pi_generator/example.dsc b/native_client_sdk/src/examples/demo/pi_generator/example.dsc index 99aefd0aab..e62adeed80 100644 --- a/native_client_sdk/src/examples/demo/pi_generator/example.dsc +++ b/native_client_sdk/src/examples/demo/pi_generator/example.dsc @@ -1,11 +1,11 @@ { - 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win', 'linux'], + 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win'], 'TARGETS': [ { 'NAME' : 'pi_generator', 'TYPE' : 'main', 'SOURCES' : ['pi_generator.cc'], - 'LIBS': ['ppapi_cpp', 'ppapi', 'pthread'] + 'LIBS': ['ppapi_simple', 'nacl_io', 'ppapi_cpp', 'ppapi', 'pthread'] } ], 'DATA': [ diff --git a/native_client_sdk/src/examples/demo/pi_generator/example.js b/native_client_sdk/src/examples/demo/pi_generator/example.js index dfc77467a4..0afee49e50 100644 --- a/native_client_sdk/src/examples/demo/pi_generator/example.js +++ b/native_client_sdk/src/examples/demo/pi_generator/example.js @@ -2,15 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Start up the paint timer when the NaCl module has loaded. -function moduleDidLoad() { - setInterval(postPaintMessage, 5); -} - -function postPaintMessage() { - common.naclModule.postMessage('paint'); -} - // 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. diff --git a/native_client_sdk/src/examples/demo/pi_generator/pi_generator.cc b/native_client_sdk/src/examples/demo/pi_generator/pi_generator.cc index 6259f0bb67..9b1f472ba9 100644 --- a/native_client_sdk/src/examples/demo/pi_generator/pi_generator.cc +++ b/native_client_sdk/src/examples/demo/pi_generator/pi_generator.cc @@ -3,333 +3,54 @@ // found in the LICENSE file. #include <math.h> -#include <pthread.h> #include <stdio.h> -#include <stdlib.h> -#include <string> - -#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" -#include "ppapi/cpp/var.h" -#include "ppapi/utility/completion_callback_factory.h" +#include "ppapi_simple/ps.h" +#include "ppapi_simple/ps_context_2d.h" +#include "ppapi_simple/ps_event.h" +#include "ppapi_simple/ps_interface.h" +#include "ppapi_simple/ps_main.h" #ifdef WIN32 #undef PostMessage -// Allow 'this' in initializer list -#pragma warning(disable : 4355) #endif 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 double kSecsPerFrame = 0.005; // How long to draw points before swapping. 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; -} // namespace - -class PiGeneratorInstance : public pp::Instance { - public: - explicit PiGeneratorInstance(PP_Instance instance); - virtual ~PiGeneratorInstance(); - - // 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::View& view); - - // 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, float device_scale); - // 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(); - - void FlushCallback(int32_t result); - - bool IsContextValid() const { return graphics_2d_context_ != NULL; } - - pp::CompletionCallbackFactory<PiGeneratorInstance> callback_factory_; - 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_; - int thread_create_result_; - double pi_; - float device_scale_; - - // 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); -}; - -// 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_) != 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(PiGeneratorInstance* image_owner) - : image_owner_(image_owner), pixels_(image_owner->LockPixels()) {} - - ~ScopedPixelLock() { - pixels_ = NULL; - image_owner_->UnlockPixels(); - } - - uint32_t* pixels() const { return pixels_; } - - private: - PiGeneratorInstance* image_owner_; // Weak reference. - uint32_t* pixels_; // Weak reference. -}; - -PiGeneratorInstance::PiGeneratorInstance(PP_Instance instance) - : pp::Instance(instance), - callback_factory_(this), - graphics_2d_context_(NULL), - pixel_buffer_(NULL), - flush_pending_(false), - quit_(false), - thread_create_result_(0), - pi_(0.0), - device_scale_(1.0) { - pthread_mutex_init(&pixel_buffer_mutex_, NULL); -} - -PiGeneratorInstance::~PiGeneratorInstance() { - quit_ = true; - if (thread_create_result_ == 0) { - 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 PiGeneratorInstance::DidChangeView(const pp::View& view) { - pp::Size size = view.GetRect().size(); - float device_scale = view.GetDeviceScale(); - size.set_width(static_cast<int>(size.width() * device_scale)); - size.set_height(static_cast<int>(size.height() * device_scale)); - if (pixel_buffer_ && size == pixel_buffer_->size() && - device_scale == device_scale_) - return; // Size and scale didn't change, no need to update anything. - - // Create a new device context with the new size and scale. - DestroyContext(); - device_scale_ = device_scale; - CreateContext(size, device_scale_); - // 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 PiGeneratorInstance::Init(uint32_t argc, - const char* argn[], - const char* argv[]) { - thread_create_result_ = - pthread_create(&compute_pi_thread_, NULL, ComputePi, this); - return thread_create_result_ == 0; -} - -uint32_t* PiGeneratorInstance::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 PiGeneratorInstance::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 PiGeneratorInstance::UnlockPixels() const { - pthread_mutex_unlock(&pixel_buffer_mutex_); -} - -void PiGeneratorInstance::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 PiGeneratorInstance::CreateContext(const pp::Size& size, - float device_scale) { - ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_); - if (!scoped_mutex.is_valid()) { - return; - } - if (IsContextValid()) - return; - graphics_2d_context_ = new pp::Graphics2D(this, size, false); - // Scale the contents of the graphics context down by the inverse of the - // device scale. This makes each pixel in the context represent a single - // physical pixel on the device when running on high-DPI displays. - // See pp::Graphics2D::SetScale for more details. - graphics_2d_context_->SetScale(1.0 / device_scale); - if (!BindGraphics(*graphics_2d_context_)) { - printf("Couldn't bind the device context\n"); - } -} -void PiGeneratorInstance::DestroyContext() { - ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_); - if (!scoped_mutex.is_valid()) { - return; - } - if (!IsContextValid()) - return; - delete graphics_2d_context_; - graphics_2d_context_ = NULL; -} +int g_points_in_circle = 0; +int g_total_points = 0; +double g_pi = 0; -void PiGeneratorInstance::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( - callback_factory_.NewCallback(&PiGeneratorInstance::FlushCallback)); -} +} // namespace -// This is called by the browser when the 2D context has been flushed to the -// browser window. -void PiGeneratorInstance::FlushCallback(int32_t result) { - set_flush_pending(false); -} +bool Render(PSContext2D_t* ctx) { + PSContext2DGetBuffer(ctx); -void* PiGeneratorInstance::ComputePi(void* param) { - int count = 0; // The number of points put inside the inscribed quadrant. - unsigned int seed = 1; - srand(seed); + if (NULL == ctx->data) + return true; - PiGeneratorInstance* pi_generator = static_cast<PiGeneratorInstance*>(param); - 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; - } + PP_TimeTicks start_time = PSInterfaceCore()->GetTimeTicks(); + while (PSInterfaceCore()->GetTimeTicks() - start_time < kSecsPerFrame) { double x = static_cast<double>(rand()) / RAND_MAX; double y = static_cast<double>(rand()) / 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]; + int px = x * ctx->width; + int py = (1.0 - y) * ctx->height; + uint32_t color = ctx->data[ctx->width * py + px]; + + ++g_total_points; if (distance < 1.0) { + ++g_points_in_circle; + g_pi = 4.0 * g_points_in_circle / g_total_points; // Set color to blue. - ++count; - pi_generator->pi_ = 4.0 * count / i; color += 4 << kBlueShift; color &= kBlueMask; } else { @@ -337,21 +58,48 @@ void* PiGeneratorInstance::ComputePi(void* param) { color += 4 << kRedShift; color &= kRedMask; } - pixel_bits[pi_generator->width() * py + px] = color | kOpaqueColorMask; + ctx->data[ctx->width * py + px] = color | kOpaqueColorMask; } - return 0; + + PSContext2DSwapBuffer(ctx); + return g_total_points != kMaxPointCount; } -class PiGeneratorModule : public pp::Module { - public: - PiGeneratorModule() : pp::Module() {} - virtual ~PiGeneratorModule() {} +/* + * Starting point for the module. We do not use main since it would + * collide with main in libppapi_cpp. + */ +int example_main(int argc, char* argv[]) { + unsigned int seed = 1; + srand(seed); + + PSEventSetFilter(PSE_ALL); + + PSContext2D_t* ctx = PSContext2DAllocate(); + bool running = true; + while (running) { + PSEvent* event; + + // Consume all available events + while ((event = PSEventTryAcquire()) != NULL) { + PSContext2DHandleEvent(ctx, event); + PSEventRelease(event); + } + + if (ctx->bound) { + running = Render(ctx); + } - virtual pp::Instance* CreateInstance(PP_Instance instance) { - return new PiGeneratorInstance(instance); + // Send the current PI value to JavaScript. + PP_Var var = PP_MakeDouble(g_pi); + PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), var); } -}; -namespace pp { -Module* CreateModule() { return new PiGeneratorModule(); } -} // namespace pp + return 0; +} + +/* + * Register the function to call once the Instance Object is initialized. + * see: pappi_simple/ps_main.h + */ +PPAPI_SIMPLE_REGISTER_MAIN(example_main); diff --git a/native_client_sdk/src/examples/demo/voronoi/example.js b/native_client_sdk/src/examples/demo/voronoi/example.js index aa02f38049..1807831141 100644 --- a/native_client_sdk/src/examples/demo/voronoi/example.js +++ b/native_client_sdk/src/examples/demo/voronoi/example.js @@ -7,7 +7,8 @@ function moduleDidLoad() { function postThreadFunc(numThreads) { return function () { - common.naclModule.postMessage('threads: ' + numThreads); + common.naclModule.postMessage({'message' : 'set_threads', + 'value' : numThreads}); } } @@ -16,34 +17,31 @@ function postThreadFunc(numThreads) { function attachListeners() { document.getElementById('benchmark').addEventListener('click', function() { - common.naclModule.postMessage('run benchmark'); + common.naclModule.postMessage({'message' : 'run_benchmark'}); common.updateStatus('BENCHMARKING... (please wait)'); }); document.getElementById('drawPoints').addEventListener('click', function() { var checked = document.getElementById('drawPoints').checked; - if (checked) - common.naclModule.postMessage('with points'); - else - common.naclModule.postMessage('without points'); + common.naclModule.postMessage({'message' : 'draw_points', + 'value' : checked}); }); document.getElementById('drawInteriors').addEventListener('click', function() { var checked = document.getElementById('drawInteriors').checked; - if (checked) - common.naclModule.postMessage('with interiors'); - else - common.naclModule.postMessage('without interiors'); + common.naclModule.postMessage({'message' : 'draw_interiors', + 'value' : checked}); }); var threads = [0, 1, 2, 4, 6, 8, 12, 16, 24, 32]; for (var i = 0; i < threads.length; i++) { - document.getElementById('radio'+i).addEventListener('click', + document.getElementById('radio' + i).addEventListener('click', postThreadFunc(threads[i])); } document.getElementById('pointRange').addEventListener('change', function() { - var value = document.getElementById('pointRange').value; - common.naclModule.postMessage('points: ' + value); + var value = parseFloat(document.getElementById('pointRange').value); + common.naclModule.postMessage({'message' : 'set_points', + 'value' : value}); document.getElementById('pointCount').textContent = value + ' points'; }); } @@ -51,9 +49,11 @@ function attachListeners() { // Handle a message coming from the NaCl module. // In the Voronoi example, the only message will be the benchmark result. function handleMessage(message_event) { - var x = (Math.round(message_event.data * 1000) / 1000).toFixed(3); - document.getElementById('result').textContent = - 'Result: ' + x + ' seconds'; - common.updateStatus('SUCCESS') + if (message_event.data['message'] == 'benchmark_result') { + var x = (Math.round(message_event.data['value'] * 1000) / 1000).toFixed(3); + document.getElementById('result').textContent = + 'Result: ' + x + ' seconds'; + common.updateStatus('SUCCESS') + } } diff --git a/native_client_sdk/src/examples/demo/voronoi/voronoi.cc b/native_client_sdk/src/examples/demo/voronoi/voronoi.cc index d4e1025181..870ffae359 100644 --- a/native_client_sdk/src/examples/demo/voronoi/voronoi.cc +++ b/native_client_sdk/src/examples/demo/voronoi/voronoi.cc @@ -13,6 +13,7 @@ #include <ppapi/cpp/rect.h> #include <ppapi/cpp/size.h> #include <ppapi/cpp/var.h> +#include <ppapi/cpp/var_dictionary.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> @@ -133,6 +134,8 @@ class Voronoi : public pp::Instance { // contents of |image_data_| to the 2D graphics context. void Update(); + // Helper to post small update messages to JS. + void PostUpdateMessage(const char* message_name, double value); // Create and initialize the 2D context used for drawing. void CreateContext(const pp::Size& size); // Destroy the 2D drawing context. @@ -466,8 +469,8 @@ void Voronoi::EndBenchmark() { benchmark_end_time_ - benchmark_start_time_); benchmarking_ = false; benchmark_frame_counter_ = 0; - pp::Var result(benchmark_end_time_ - benchmark_start_time_); - PostMessage(result); + double result = benchmark_end_time_ - benchmark_start_time_; + PostUpdateMessage("benchmark_result", result); } // Handle input events from the user. @@ -488,30 +491,35 @@ bool Voronoi::HandleInputEvent(const pp::InputEvent& event) { } // Handle messages sent from Javascript. -void Voronoi::HandleMessage(const pp::Var& message) { - if (message.is_string()) { - std::string message_string = message.AsString(); - if (message_string == "run benchmark" && !benchmarking_) +void Voronoi::HandleMessage(const pp::Var& var) { + if (var.is_dictionary()) { + pp::VarDictionary dictionary(var); + std::string message = dictionary.Get("message").AsString(); + if (message == "run_benchmark" && !benchmarking_) StartBenchmark(); - else if (message_string == "with points") - draw_points_ = true; - else if (message_string == "without points") - draw_points_ = false; - else if (message_string == "with interiors") - draw_interiors_ = true; - else if (message_string == "without interiors") - draw_interiors_ = false; - else if (strstr(message_string.c_str(), "points:")) { - int num_points = atoi(strstr(message_string.c_str(), " ")); + else if (message == "draw_points") + draw_points_ = dictionary.Get("value").AsBool(); + else if (message == "draw_interiors") + draw_interiors_ = dictionary.Get("value").AsBool(); + else if (message == "set_points") { + int num_points = dictionary.Get("value").AsInt(); point_count_ = std::min(kMaxPointCount, std::max(0, num_points)); - } else if (strstr(message_string.c_str(), "threads:")) { - int thread_count = atoi(strstr(message_string.c_str(), " ")); + } else if (message == "set_threads") { + int thread_count = dictionary.Get("value").AsInt(); delete workers_; workers_ = new ThreadPool(thread_count); } } } +// PostUpdateMessage() helper function for sendimg small messages to JS. +void Voronoi::PostUpdateMessage(const char* message_name, double value) { + pp::VarDictionary message; + message.Set("message", message_name); + message.Set("value", value); + PostMessage(message); +} + void Voronoi::FlushCallback(void* thiz, int32_t result) { static_cast<Voronoi*>(thiz)->Update(); } diff --git a/native_client_sdk/src/examples/getting_started/hello_world/index.html b/native_client_sdk/src/examples/getting_started/hello_world/index.html index 4ed29868dd..32da7732cc 100644 --- a/native_client_sdk/src/examples/getting_started/hello_world/index.html +++ b/native_client_sdk/src/examples/getting_started/hello_world/index.html @@ -17,8 +17,8 @@ found in the LICENSE file. <h2>Status: <code id="statusField">NO-STATUS</code></h2> <p>The Hello World In C example demonstrates the basic structure of all Native Client applications. This example loads a Native Client module. The - page tracks the status of the module as it load. On a successful load, the - module will post a message containing the string "Hello World" back to + page tracks the status of the module as it loads. On a successful load, + the module will post a message containing the string "Hello World" back to JavaScript which will display it as an alert.</p> <h2>Output:</h2> <pre id="log" style="font-weight: bold"></pre> diff --git a/native_client_sdk/src/examples/getting_started/simple_hello_world/index.html b/native_client_sdk/src/examples/getting_started/simple_hello_world/index.html index c6a4aa1ef4..5e2965ff93 100644 --- a/native_client_sdk/src/examples/getting_started/simple_hello_world/index.html +++ b/native_client_sdk/src/examples/getting_started/simple_hello_world/index.html @@ -16,7 +16,7 @@ found in the LICENSE file. <h1>{{title}}</h1> <h2>Status: <code id="statusField">NO-STATUS</code></h2> <p>The Hello World Stdio example is the simplest one in the SDK. It uses the - ppapi_main library which creates an Module and Instance, using default + ppapi_main library which creates a Module and an Instance, using default values to simplify setup and communication with the PPAPI system. In addition, it uses the nacl_io library to remap IO to the Pepper API. This simplifies IO by providing a standard blocking API and allowing STDERR to diff --git a/native_client_sdk/src/examples/tutorial/dlopen/index.html b/native_client_sdk/src/examples/tutorial/dlopen/index.html index 0e535b4c66..95bc51d58e 100644 --- a/native_client_sdk/src/examples/tutorial/dlopen/index.html +++ b/native_client_sdk/src/examples/tutorial/dlopen/index.html @@ -15,7 +15,7 @@ found in the LICENSE file. <body {{attrs}}> <h1>{{title}}</h1> <h2>Status: <code id="statusField">NO-STATUS</code></h2> - <p>The dlopen example demonstrates how build dynamic libraries and then + <p>The dlopen example demonstrates how to build dynamic libraries and then open and use them at runtime. When the page loads, type in a question and hit enter or click the ASK! button. The question and answer will be displayed in the page under the text entry box. Shared libraries are only diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_event_listener.cc b/native_client_sdk/src/libraries/gtest_ppapi/gtest_event_listener.cc deleted file mode 100644 index 3bc84a8494..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_event_listener.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2012 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. -#include "gtest_ppapi/gtest_event_listener.h" - -#include "ppapi/c/pp_macros.h" -#include "ppapi/cpp/instance.h" -#include "ppapi/cpp/module.h" -#include "ppapi/cpp/var.h" - -#if defined(WIN32) -#undef PostMessage -#endif - -GTestEventListener::GTestEventListener(pp::Instance* instance) - : instance_(instance), - PP_ALLOW_THIS_IN_INITIALIZER_LIST(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 << '.'; - MyPostMessage(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::"; - if (test_part_result.file_name()) - msg << test_part_result.file_name(); - else - msg << "<unknown filename>"; - msg << "::" << test_part_result.line_number() << "::"; - msg << test_part_result.summary(); - MyPostMessage(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"); - MyPostMessage(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 << "::"; - MyPostMessage(msg.str()); -} - -void GTestEventListener::MyPostMessage(const std::string& str) { - if (pp::Module::Get()->core()->IsMainThread()) { - instance_->PostMessage(str); - } else { - pp::CompletionCallback cc = factory_.NewCallback( - >estEventListener::MyPostMessageCallback, str); - pp::Module::Get()->core()->CallOnMainThread(0, cc, PP_OK); - } -} - -void GTestEventListener::MyPostMessageCallback(int32_t result, - const std::string& str) { - instance_->PostMessage(str); -} diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_event_listener.h b/native_client_sdk/src/libraries/gtest_ppapi/gtest_event_listener.h deleted file mode 100644 index 90d5d0a71c..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_event_listener.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2012 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. -#ifndef GTEST_PPAPI_GTEST_EVENT_LISTENER_H_ -#define GTEST_PPAPI_GTEST_EVENT_LISTENER_H_ - -#include <string> - -#include "gtest/gtest.h" -#include "ppapi/utility/completion_callback_factory.h" - -namespace pp { -class Instance; -} // namespace pp - -// 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: - // Called MyPostMessage as to not conflict with win32 macro PostMessage. - void MyPostMessage(const std::string& str); - void MyPostMessageCallback(int32_t result, const std::string& str); - - pp::Instance* instance_; - pp::CompletionCallbackFactory<GTestEventListener> factory_; -}; - -#endif // GTEST_PPAPI_GTEST_EVENT_LISTENER_H_ diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_instance.cc b/native_client_sdk/src/libraries/gtest_ppapi/gtest_instance.cc deleted file mode 100644 index dc5e1a72c8..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_instance.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2012 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. - -#include "gtest_ppapi/gtest_instance.h" -#include "gtest_ppapi/gtest_runner.h" -#include "ppapi/cpp/var.h" - -#if defined(WIN32) -#undef PostMessage -#endif - -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; - 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. - GTestRunner::gtest_runner()->RunAllTests(); - } else { - std::string return_var; - return_var = "Unknown message "; - return_var += message; - PostMessage(return_var); - } -} diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_instance.h b/native_client_sdk/src/libraries/gtest_ppapi/gtest_instance.h deleted file mode 100644 index 152f3bea7b..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_instance.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2012 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. -#ifndef GTEST_PPAPI_GTEST_INSTANCE_H_ -#define GTEST_PPAPI_GTEST_INSTANCE_H_ - -#include "ppapi/cpp/instance.h" - -// 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); -}; - -#endif // GTEST_PPAPI_GTEST_INSTANCE_H_ diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_module.cc b/native_client_sdk/src/libraries/gtest_ppapi/gtest_module.cc deleted file mode 100644 index bda434cb7a..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_module.cc +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2012 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. -#include "gtest_ppapi/gtest_module.h" -#include "gtest_ppapi/gtest_instance.h" - -pp::Instance* GTestModule::CreateInstance(PP_Instance instance) { - return new GTestInstance(instance); -} diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_module.h b/native_client_sdk/src/libraries/gtest_ppapi/gtest_module.h deleted file mode 100644 index b08b90aa56..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_module.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2012 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. -#ifndef GTEST_PPAPI_GTEST_MODULE_H_ -#define GTEST_PPAPI_GTEST_MODULE_H_ -#include "ppapi/cpp/module.h" - -// 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); -}; - -#endif // GTEST_PPAPI_GTEST_MODULE_H_ diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_nacl_environment.cc b/native_client_sdk/src/libraries/gtest_ppapi/gtest_nacl_environment.cc deleted file mode 100644 index 5cc51ec15c..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_nacl_environment.cc +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2012 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. -#include "gtest_ppapi/gtest_nacl_environment.h" - -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() { -} diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_nacl_environment.h b/native_client_sdk/src/libraries/gtest_ppapi/gtest_nacl_environment.h deleted file mode 100644 index 759b775d2e..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_nacl_environment.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef GTEST_PPAPI_GTEST_NACL_ENVIRONMENT_H_ -#define GTEST_PPAPI_GTEST_NACL_ENVIRONMENT_H_ - -#include <cassert> -#include "gtest/gtest.h" - -namespace pp { -class Instance; -} // namespace pp - -// 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_; -}; - -#endif // GTEST_PPAPI_GTEST_NACL_ENVIRONMENT_H_ diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_runner.cc b/native_client_sdk/src/libraries/gtest_ppapi/gtest_runner.cc deleted file mode 100644 index d8ce6dc04b..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_runner.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2012 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. -#include "gtest_ppapi/gtest_runner.h" - -#include <cassert> - -#include "gtest/gtest.h" -#include "gtest_ppapi/gtest_event_listener.h" -#include "gtest_ppapi/gtest_nacl_environment.h" - -pthread_t GTestRunner::g_test_runner_thread_; -GTestRunner* GTestRunner::gtest_runner_ = NULL; - -void GTestRunner::CreateGTestRunnerThread(pp::Instance* instance, - int argc, char** argv) { - assert(!gtest_runner_); - if (!gtest_runner_) { - 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; - 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 GTestEventListener(instance)); - - // We use our own gtest environment, mainly to make the nacl instance - // available to the individual unit tests. - GTestNaclEnvironment* test_env = new 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(); - int result = RUN_ALL_TESTS(); - (void)result; -} diff --git a/native_client_sdk/src/libraries/gtest_ppapi/gtest_runner.h b/native_client_sdk/src/libraries/gtest_ppapi/gtest_runner.h deleted file mode 100644 index b7e489c048..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/gtest_runner.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2012 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. -#ifndef GTEST_PPAPI_GTEST_RUNNER_H_ -#define GTEST_PPAPI_GTEST_RUNNER_H_ - -#include <pthread.h> -#include "gtest_ppapi/thread_condition.h" - -namespace pp { -class Instance; -} // namespace pp - -// 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 mode. - // - // 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 }; - ThreadCondition status_signal_; - Status status_; - - static pthread_t g_test_runner_thread_; - static GTestRunner* gtest_runner_; -}; - -#endif // GTEST_PPAPI_GTEST_RUNNER_H_ diff --git a/native_client_sdk/src/libraries/gtest_ppapi/library.dsc b/native_client_sdk/src/libraries/gtest_ppapi/library.dsc deleted file mode 100644 index 01aa35067b..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/library.dsc +++ /dev/null @@ -1,33 +0,0 @@ -{ - 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win', 'linux'], - 'TARGETS': [ - { - 'NAME' : 'gtest_ppapi', - # gtest-typed-test.h:239:47: error: anonymous variadic macros were introduced in C99 [-Werror=variadic-macros] - 'CXXFLAGS': ['-Wno-variadic-macros'], - 'TYPE' : 'lib', - 'SOURCES' : [ - "gtest_event_listener.cc", - "gtest_instance.cc", - "gtest_module.cc", - "gtest_nacl_environment.cc", - "gtest_runner.cc", - ], - } - ], - 'HEADERS': [ - { - 'FILES': [ - "gtest_event_listener.h", - "gtest_instance.h", - "gtest_module.h", - "gtest_nacl_environment.h", - "gtest_runner.h", - "thread_condition.h", - ], - 'DEST': 'include/gtest_ppapi', - }, - ], - 'DEST': 'testlibs', - 'NAME': 'gtest_ppapi', -} diff --git a/native_client_sdk/src/libraries/gtest_ppapi/thread_condition.h b/native_client_sdk/src/libraries/gtest_ppapi/thread_condition.h deleted file mode 100644 index d7af353349..0000000000 --- a/native_client_sdk/src/libraries/gtest_ppapi/thread_condition.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef GTEST_PPAPI_THREAD_CONDITION_H_ -#define GTEST_PPAPI_THREAD_CONDITION_H_ - -#include <pthread.h> - -struct timespec; - -// 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 condition 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_); - } - - private: - pthread_mutex_t cond_mutex_; - pthread_cond_t condition_; -}; - -#endif // GTEST_PPAPI_THREAD_CONDITION_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/ioctl.h b/native_client_sdk/src/libraries/nacl_io/ioctl.h new file mode 100644 index 0000000000..0a730e8568 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/ioctl.h @@ -0,0 +1,25 @@ +/* Copyright 2013 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. + */ +#ifndef LIBRARIES_NACL_IO_IOCTL_H_ +#define LIBRARIES_NACL_IO_IOCTL_H_ + +/* ioctl to tell a tty mount to prefix every message with a particular + * null-terminated string. Accepts a pointer to a C string which will + * be the prefix. + */ +#define TIOCNACLPREFIX 0xadcd01 + +/* ioctl to feed input to a tty mount. Accepts a pointer to the following + * struct (tioc_nacl_input_string), which contains a pointer to an array + * of characters. + */ +#define TIOCNACLINPUT 0xadcd02 + +struct tioc_nacl_input_string { + size_t length; + const char* buffer; +}; + +#endif // LIBRARIES_NACL_IO_NACL_IO_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc index a943894595..322bd9e4af 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc @@ -8,16 +8,15 @@ #include <fcntl.h> #include <pthread.h> -#ifndef WIN32 -// Needed for SEEK_SET/SEEK_CUR/SEEK_END. -#include <unistd.h> -#endif - #include "nacl_io/mount.h" #include "nacl_io/mount_node.h" +#include "nacl_io/osunistd.h" // It is only legal to construct a handle while the kernel lock is held. -KernelHandle::KernelHandle(Mount* mnt, MountNode* node) +KernelHandle::KernelHandle() + : mount_(NULL), node_(NULL), offs_(0) {} + +KernelHandle::KernelHandle(const ScopedMount& mnt, const ScopedMountNode& node) : mount_(mnt), node_(node), offs_(0) {} Error KernelHandle::Init(int open_mode) { @@ -71,3 +70,4 @@ Error KernelHandle::Seek(off_t offset, int whence, off_t* out_offset) { *out_offset = offs_ = new_offset; return 0; } + diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h index ab7b7cd699..d24533d5ff 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h @@ -8,37 +8,37 @@ #include <pthread.h> #include "nacl_io/error.h" +#include "nacl_io/mount.h" +#include "nacl_io/mount_node.h" #include "nacl_io/ostypes.h" + #include "sdk_util/macros.h" #include "sdk_util/ref_object.h" - -class Mount; -class MountNode; +#include "sdk_util/scoped_ref.h" // KernelHandle provides a reference counted container for the open // file information, such as it's mount, node, access type and offset. // KernelHandle can only be referenced when the KernelProxy lock is held. class KernelHandle : public RefObject { public: - // Assumes |mnt| and |node| are non-NULL. - KernelHandle(Mount* mnt, MountNode* node); + KernelHandle(); + KernelHandle(const ScopedMount& mnt, const ScopedMountNode& node); Error Init(int open_flags); + // Assumes |out_offset| is non-NULL. Error Seek(off_t offset, int whence, off_t* out_offset); - Mount* mount_; - MountNode* node_; + ScopedRef<Mount> mount_; + ScopedRef<MountNode> node_; size_t offs_; private: - // May only be called by the KernelProxy when the Kernel's - // lock is held. friend class KernelObject; friend class KernelProxy; - void Acquire() { RefObject::Acquire(); } - bool Release() { return RefObject::Release(); } DISALLOW_COPY_AND_ASSIGN(KernelHandle); }; +typedef ScopedRef<KernelHandle> ScopedKernelHandle; + #endif // LIBRARIES_NACL_IO_KERNEL_HANDLE_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc index 9615dde48b..b237221b3f 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc @@ -172,3 +172,23 @@ int ki_munmap(void* addr, size_t length) { int ki_open_resource(const char* file) { return s_kp->open_resource(file); } + +int ki_ioctl(int d, int request, char* argp) { + return s_kp->ioctl(d, request, argp); +} + +int ki_chown(const char* path, uid_t owner, gid_t group) { + return s_kp->chown(path, owner, group); +} + +int ki_fchown(int fd, uid_t owner, gid_t group) { + return s_kp->fchown(fd, owner, group); +} + +int ki_lchown(const char* path, uid_t owner, gid_t group) { + return s_kp->lchown(path, owner, group); +} + +int ki_utime(const char* filename, const struct utimbuf* times) { + return s_kp->utime(filename, times); +} diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h index c4b658dfd4..de9e98e33c 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h @@ -8,6 +8,7 @@ #include <ppapi/c/ppb.h> #include <ppapi/c/pp_instance.h> #include "nacl_io/ostypes.h" +#include "nacl_io/osutime.h" #include "sdk_util/macros.h" EXTERN_C_BEGIN @@ -56,8 +57,12 @@ void* ki_mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset); int ki_munmap(void* addr, size_t length); int ki_open_resource(const char* file); +int ki_ioctl(int d, int request, char* argp); +int ki_chown(const char* path, uid_t owner, gid_t group); +int ki_fchown(int fd, uid_t owner, gid_t group); +int ki_lchown(const char* path, uid_t owner, gid_t group); +int ki_utime(const char* filename, const struct utimbuf* times); EXTERN_C_END #endif // LIBRARIES_NACL_IO_KERNEL_INTERCEPT_H_ - diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_object.cc b/native_client_sdk/src/libraries/nacl_io/kernel_object.cc index 6be3c9779b..f593d21ce2 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_object.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_object.cc @@ -17,7 +17,10 @@ #include "nacl_io/kernel_handle.h" #include "nacl_io/mount.h" #include "nacl_io/mount_node.h" + #include "sdk_util/auto_lock.h" +#include "sdk_util/ref_object.h" +#include "sdk_util/scoped_ref.h" KernelObject::KernelObject() { pthread_mutex_init(&kernel_lock_, NULL); @@ -32,11 +35,11 @@ KernelObject::~KernelObject() { // Uses longest prefix to find the mount for the give path, then // acquires the mount and returns it with a relative path. Error KernelObject::AcquireMountAndPath(const std::string& relpath, - Mount** out_mount, + ScopedMount* out_mount, Path* out_path) { - *out_mount = NULL; - *out_path = Path(); + out_mount->reset(NULL); + *out_path = Path(); Path abs_path; { AutoLock lock(&process_lock_); @@ -44,7 +47,6 @@ Error KernelObject::AcquireMountAndPath(const std::string& relpath, } AutoLock lock(&kernel_lock_); - Mount* mount = NULL; // Find longest prefix size_t max = abs_path.Size(); @@ -53,66 +55,32 @@ Error KernelObject::AcquireMountAndPath(const std::string& relpath, if (it != mounts_.end()) { out_path->Set("/"); out_path->Append(abs_path.Range(max - len, max)); - mount = it->second; - break; + + *out_mount = it->second; + return 0; } } - if (NULL == mount) - return ENOTDIR; - - // Acquire the mount while we hold the proxy lock - mount->Acquire(); - *out_mount = mount; - return 0; -} - -void KernelObject::ReleaseMount(Mount* mnt) { - AutoLock lock(&kernel_lock_); - mnt->Release(); + return ENOTDIR; } -Error KernelObject::AcquireHandle(int fd, KernelHandle** out_handle) { - *out_handle = NULL; +Error KernelObject::AcquireHandle(int fd, ScopedKernelHandle* out_handle) { + out_handle->reset(NULL); AutoLock lock(&process_lock_); if (fd < 0 || fd >= static_cast<int>(handle_map_.size())) return EBADF; - KernelHandle* handle = handle_map_[fd]; - if (NULL == handle) - return EBADF; - - // Ref count while holding parent mutex - handle->Acquire(); - - lock.Unlock(); - if (handle->node_) - handle->mount_->AcquireNode(handle->node_); - - *out_handle = handle; - return 0; -} - -void KernelObject::ReleaseHandle(KernelHandle* handle) { - // The handle must already be held before taking the - // kernel lock. - if (handle->node_) - handle->mount_->ReleaseNode(handle->node_); + *out_handle = handle_map_[fd]; + if (out_handle) return 0; - AutoLock lock(&process_lock_); - handle->Release(); + return EBADF; } -int KernelObject::AllocateFD(KernelHandle* handle) { +int KernelObject::AllocateFD(const ScopedKernelHandle& handle) { AutoLock lock(&process_lock_); int id; - // Acquire the handle and its mount since we are about to track it with - // this FD. - handle->Acquire(); - handle->mount_->Acquire(); - // If we can recycle and FD, use that first if (free_fds_.size()) { id = free_fds_.front(); @@ -127,28 +95,16 @@ int KernelObject::AllocateFD(KernelHandle* handle) { return id; } -void KernelObject::FreeAndReassignFD(int fd, KernelHandle* handle) { +void KernelObject::FreeAndReassignFD(int fd, const ScopedKernelHandle& handle) { if (NULL == handle) { FreeFD(fd); } else { AutoLock lock(&process_lock_); - // Acquire the new handle first in case they are the same. - if (handle) { - handle->Acquire(); - handle->mount_->Acquire(); - } - // If the required FD is larger than the current set, grow the set - if (fd >= handle_map_.size()) { - handle_map_.resize(fd + 1); - } else { - KernelHandle* old_handle = handle_map_[fd]; - if (NULL != old_handle) { - old_handle->mount_->Release(); - old_handle->Release(); - } - } + if (fd >= handle_map_.size()) + handle_map_.resize(fd + 1, ScopedRef<KernelHandle>()); + handle_map_[fd] = handle; } } @@ -156,14 +112,9 @@ void KernelObject::FreeAndReassignFD(int fd, KernelHandle* handle) { void KernelObject::FreeFD(int fd) { AutoLock lock(&process_lock_); - // Release the mount and handle since we no longer - // track them with this FD. - KernelHandle* handle = handle_map_[fd]; - handle->Release(); - handle->mount_->Release(); - - handle_map_[fd] = NULL; + handle_map_[fd].reset(NULL); free_fds_.push_back(fd); + // Force lower numbered FD to be available first. std::push_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>()); } @@ -171,6 +122,7 @@ void KernelObject::FreeFD(int fd) { Path KernelObject::GetAbsPathLocked(const std::string& path) { // Generate absolute path Path abs_path(cwd_); + if (path[0] == '/') { abs_path = path; } else { @@ -180,3 +132,4 @@ Path KernelObject::GetAbsPathLocked(const std::string& path) { return abs_path; } + diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_object.h b/native_client_sdk/src/libraries/nacl_io/kernel_object.h index eb4c8ac041..6dc695999a 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_object.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_object.h @@ -12,10 +12,10 @@ #include <vector> #include "nacl_io/error.h" +#include "nacl_io/kernel_handle.h" +#include "nacl_io/mount.h" #include "nacl_io/path.h" -class KernelHandle; -class Mount; // KernelObject provides basic functionality for threadsafe // access to kernel objects such as file descriptors and @@ -23,8 +23,8 @@ class Mount; // path resolution. class KernelObject { public: - typedef std::vector<KernelHandle*> HandleMap_t; - typedef std::map<std::string, Mount*> MountMap_t; + typedef std::vector<ScopedKernelHandle> HandleMap_t; + typedef std::map<std::string, ScopedMount> MountMap_t; KernelObject(); virtual ~KernelObject(); @@ -32,23 +32,20 @@ class KernelObject { // Find the mount for the given path, and acquires it. // Assumes |out_mount| and |out_path| are non-NULL. Error AcquireMountAndPath(const std::string& relpath, - Mount** out_mount, + ScopedMount* out_mount, Path* out_path); - // Assumes |mnt| is non-NULL. - void ReleaseMount(Mount* mnt); // Convert from FD to KernelHandle, and acquire the handle. // Assumes |out_handle| is non-NULL. - Error AcquireHandle(int fd, KernelHandle** out_handle); - // Assumes |handle| is non-NULL. - void ReleaseHandle(KernelHandle* handle); + Error AcquireHandle(int fd, ScopedKernelHandle* out_handle); // Allocate a new fd and assign the handle to it, while // ref counting the handle and associated mount. // Assumes |handle| is non-NULL; - int AllocateFD(KernelHandle* handle); + int AllocateFD(const ScopedKernelHandle& handle); + // Assumes |handle| is non-NULL; - void FreeAndReassignFD(int fd, KernelHandle* handle); + void FreeAndReassignFD(int fd, const ScopedKernelHandle& handle); void FreeFD(int fd); protected: diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc index 2f49395ee0..8d5655ab59 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc @@ -25,6 +25,7 @@ #include "nacl_io/osstat.h" #include "nacl_io/path.h" #include "nacl_io/pepper_interface.h" +#include "nacl_io/typed_mount_factory.h" #include "sdk_util/auto_lock.h" #include "sdk_util/ref_object.h" @@ -38,18 +39,27 @@ KernelProxy::KernelProxy() : dev_(0), ppapi_(NULL) {} -KernelProxy::~KernelProxy() { delete ppapi_; } +KernelProxy::~KernelProxy() { + // Clean up the MountFactories. + for (MountFactoryMap_t::iterator i = factories_.begin(); + i != factories_.end(); + ++i) { + delete i->second; + } + + delete ppapi_; +} void KernelProxy::Init(PepperInterface* ppapi) { ppapi_ = ppapi; cwd_ = "/"; dev_ = 1; - factories_["memfs"] = MountMem::Create<MountMem>; - factories_["dev"] = MountDev::Create<MountDev>; - factories_["html5fs"] = MountHtml5Fs::Create<MountHtml5Fs>; - factories_["httpfs"] = MountHttp::Create<MountHttp>; - factories_["passthroughfs"] = MountPassthrough::Create<MountPassthrough>; + factories_["memfs"] = new TypedMountFactory<MountMem>; + factories_["dev"] = new TypedMountFactory<MountDev>; + factories_["html5fs"] = new TypedMountFactory<MountHtml5Fs>; + factories_["httpfs"] = new TypedMountFactory<MountHttp>; + factories_["passthroughfs"] = new TypedMountFactory<MountPassthrough>; int result; result = mount("", "/", "passthroughfs", 0, NULL); @@ -67,75 +77,52 @@ void KernelProxy::Init(PepperInterface* ppapi) { int KernelProxy::open(const char* path, int oflags) { Path rel; - Mount* mnt; + ScopedMount mnt; Error error = AcquireMountAndPath(path, &mnt, &rel); if (error) { errno = error; return -1; } - MountNode* node = NULL; + ScopedMountNode node; error = mnt->Open(rel, oflags, &node); if (error) { errno = error; - ReleaseMount(mnt); return -1; } - KernelHandle* handle = new KernelHandle(mnt, node); + ScopedKernelHandle handle(new KernelHandle(mnt, node)); error = handle->Init(oflags); if (error) { errno = error; - ReleaseMount(mnt); return -1; } - int fd = AllocateFD(handle); - mnt->AcquireNode(node); - - ReleaseHandle(handle); - ReleaseMount(mnt); - - return fd; + return AllocateFD(handle); } int KernelProxy::close(int fd) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; return -1; } - Mount* mount = handle->mount_; - // Acquire the mount to ensure FreeFD doesn't prematurely destroy it. - mount->Acquire(); - - // FreeFD will release the handle/mount held by this fd. + // Remove the FD from the process open file descriptor map FreeFD(fd); - - // If this handle is the last reference to its node, releasing it will close - // the node. - ReleaseHandle(handle); - - // Finally, release the mount. - mount->Release(); - return 0; } int KernelProxy::dup(int oldfd) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(oldfd, &handle); if (error) { errno = error; return -1; } - int newfd = AllocateFD(handle); - ReleaseHandle(handle); - - return newfd; + return AllocateFD(handle); } int KernelProxy::dup2(int oldfd, int newfd) { @@ -143,7 +130,7 @@ int KernelProxy::dup2(int oldfd, int newfd) { if (oldfd == newfd) return newfd; - KernelHandle* old_handle; + ScopedKernelHandle old_handle; Error error = AcquireHandle(oldfd, &old_handle); if (error) { errno = error; @@ -151,7 +138,6 @@ int KernelProxy::dup2(int oldfd, int newfd) { } FreeAndReassignFD(newfd, old_handle); - ReleaseHandle(old_handle); return newfd; } @@ -190,7 +176,7 @@ char* KernelProxy::getwd(char* buf) { } int KernelProxy::chmod(const char* path, mode_t mode) { - int fd = KernelProxy::open(path, O_RDWR); + int fd = KernelProxy::open(path, O_RDONLY); if (-1 == fd) return -1; @@ -199,8 +185,24 @@ int KernelProxy::chmod(const char* path, mode_t mode) { return result; } +int KernelProxy::chown(const char* path, uid_t owner, gid_t group) { + return 0; +} + +int KernelProxy::fchown(int fd, uid_t owner, gid_t group) { + return 0; +} + +int KernelProxy::lchown(const char* path, uid_t owner, gid_t group) { + return 0; +} + +int KernelProxy::utime(const char* filename, const struct utimbuf* times) { + return 0; +} + int KernelProxy::mkdir(const char* path, mode_t mode) { - Mount* mnt; + ScopedMount mnt; Path rel; Error error = AcquireMountAndPath(path, &mnt, &rel); if (error) { @@ -208,19 +210,17 @@ int KernelProxy::mkdir(const char* path, mode_t mode) { return -1; } - int result = 0; error = mnt->Mkdir(rel, mode); if (error) { errno = error; - result = -1; + return -1; } - ReleaseMount(mnt); - return result; + return 0; } int KernelProxy::rmdir(const char* path) { - Mount* mnt; + ScopedMount mnt; Path rel; Error error = AcquireMountAndPath(path, &mnt, &rel); if (error) { @@ -228,15 +228,13 @@ int KernelProxy::rmdir(const char* path) { return -1; } - int result = 0; error = mnt->Rmdir(rel); if (error) { errno = error; - result = -1; + return -1; } - ReleaseMount(mnt); - return result; + return 0; } int KernelProxy::stat(const char* path, struct stat* buf) { @@ -313,14 +311,13 @@ int KernelProxy::mount(const char* source, free(str); } - Mount* mnt = NULL; - Error error = factory->second(dev_++, smap, ppapi_, &mnt); + Error error = + factory->second->CreateMount(dev_++, smap, ppapi_, &mounts_[abs_targ]); if (error) { errno = error; return -1; } - mounts_[abs_targ] = mnt; return 0; } @@ -346,13 +343,12 @@ int KernelProxy::umount(const char* path) { return -1; } - it->second->Release(); mounts_.erase(it); return 0; } ssize_t KernelProxy::read(int fd, void* buf, size_t nbytes) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; @@ -362,18 +358,19 @@ ssize_t KernelProxy::read(int fd, void* buf, size_t nbytes) { AutoLock lock(&handle->lock_); int cnt = 0; error = handle->node_->Read(handle->offs_, buf, nbytes, &cnt); - if (error) + if (error) { errno = error; + return -1; + } if (cnt > 0) handle->offs_ += cnt; - ReleaseHandle(handle); return cnt; } ssize_t KernelProxy::write(int fd, const void* buf, size_t nbytes) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; @@ -383,37 +380,36 @@ ssize_t KernelProxy::write(int fd, const void* buf, size_t nbytes) { AutoLock lock(&handle->lock_); int cnt = 0; error = handle->node_->Write(handle->offs_, buf, nbytes, &cnt); - if (error) + if (error) { errno = error; + return -1; + } if (cnt > 0) handle->offs_ += cnt; - ReleaseHandle(handle); return cnt; } int KernelProxy::fstat(int fd, struct stat* buf) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; return -1; } - int result = 0; error = handle->node_->GetStat(buf); if (error) { errno = error; - result = -1; + return -1; } - ReleaseHandle(handle); - return result; + return 0; } int KernelProxy::getdents(int fd, void* buf, unsigned int count) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; @@ -430,69 +426,79 @@ int KernelProxy::getdents(int fd, void* buf, unsigned int count) { if (cnt > 0) handle->offs_ += cnt; - ReleaseHandle(handle); return cnt; } int KernelProxy::ftruncate(int fd, off_t length) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; return -1; } - int result = 0; error = handle->node_->FTruncate(length); if (error) { errno = error; - result = -1; + return -1; } - ReleaseHandle(handle); - return result; + return 0; } int KernelProxy::fsync(int fd) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; return -1; } - int result = 0; error = handle->node_->FSync(); if (error) { errno = error; - result = -1; + return -1; } - ReleaseHandle(handle); - return result; + return 0; } int KernelProxy::isatty(int fd) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; return -1; } - int result = 0; error = handle->node_->IsaTTY(); if (error) { errno = error; - result = -1; + return -1; } - ReleaseHandle(handle); - return result; + return 0; +} + +int KernelProxy::ioctl(int d, int request, char* argp) { + ScopedKernelHandle handle; + Error error = AcquireHandle(d, &handle); + if (error) { + errno = error; + return -1; + } + + error = handle->node_->Ioctl(request, argp); + if (error) { + errno = error; + return -1; + } + + return 0; } off_t KernelProxy::lseek(int fd, off_t offset, int whence) { - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; @@ -504,15 +510,14 @@ off_t KernelProxy::lseek(int fd, off_t offset, int whence) { error = handle->Seek(offset, whence, &new_offset); if (error) { errno = error; - new_offset = -1; + return -1; } - ReleaseHandle(handle); return new_offset; } int KernelProxy::unlink(const char* path) { - Mount* mnt; + ScopedMount mnt; Path rel; Error error = AcquireMountAndPath(path, &mnt, &rel); if (error) { @@ -520,19 +525,17 @@ int KernelProxy::unlink(const char* path) { return -1; } - int result = 0; error = mnt->Unlink(rel); if (error) { errno = error; - result = -1; + return -1; } - ReleaseMount(mnt); - return result; + return 0; } int KernelProxy::remove(const char* path) { - Mount* mnt; + ScopedMount mnt; Path rel; Error error = AcquireMountAndPath(path, &mnt, &rel); if (error) { @@ -540,28 +543,46 @@ int KernelProxy::remove(const char* path) { return -1; } - int result = 0; error = mnt->Remove(rel); if (error) { errno = error; - result = -1; + return -1; } - ReleaseMount(mnt); - return result; + return 0; } // TODO(noelallen): Needs implementation. int KernelProxy::fchmod(int fd, int mode) { - errno = EINVAL; - return -1; + ScopedKernelHandle handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } + + return 0; } int KernelProxy::access(const char* path, int amode) { - errno = EINVAL; - return -1; + Path rel; + + ScopedMount mnt; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + error = mnt->Access(rel, amode); + if (error) { + errno = error; + return -1; + } + return 0; } +// TODO(noelallen): Needs implementation. int KernelProxy::link(const char* oldpath, const char* newpath) { errno = EINVAL; return -1; @@ -582,7 +603,7 @@ void* KernelProxy::mmap(void* addr, assert((flags & MAP_ANONYMOUS) == 0); assert(fd != -1); - KernelHandle* handle; + ScopedKernelHandle handle; Error error = AcquireHandle(fd, &handle); if (error) { errno = error; @@ -590,17 +611,13 @@ void* KernelProxy::mmap(void* addr, } void* new_addr; - { - AutoLock lock(&handle->lock_); - error = handle->node_->MMap(addr, length, prot, flags, offset, &new_addr); - if (error) { - errno = error; - ReleaseHandle(handle); - return MAP_FAILED; - } + AutoLock lock(&handle->lock_); + error = handle->node_->MMap(addr, length, prot, flags, offset, &new_addr); + if (error) { + errno = error; + return MAP_FAILED; } - ReleaseHandle(handle); return new_addr; } @@ -640,7 +657,7 @@ int KernelProxy::munmap(void* addr, size_t length) { } int KernelProxy::open_resource(const char* path) { - Mount* mnt; + ScopedMount mnt; Path rel; Error error = AcquireMountAndPath(path, &mnt, &rel); if (error) { @@ -648,31 +665,24 @@ int KernelProxy::open_resource(const char* path) { return -1; } - MountNode* node = NULL; + ScopedMountNode node; error = mnt->OpenResource(rel, &node); if (error) { // OpenResource failed, try Open(). error = mnt->Open(rel, O_RDONLY, &node); if (error) { errno = error; - ReleaseMount(mnt); return -1; } } - KernelHandle* handle = new KernelHandle(mnt, node); + ScopedKernelHandle handle(new KernelHandle(mnt, node)); error = handle->Init(O_RDONLY); if (error) { errno = error; - ReleaseMount(mnt); return -1; } - int fd = AllocateFD(handle); - mnt->AcquireNode(node); - - ReleaseHandle(handle); - ReleaseMount(mnt); - - return fd; + return AllocateFD(handle); } + diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h index e8c251a32a..554a75ca4c 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h @@ -5,21 +5,14 @@ #ifndef LIBRARIES_NACL_IO_KERNEL_PROXY_H_ #define LIBRARIES_NACL_IO_KERNEL_PROXY_H_ -#include <ppapi/c/pp_instance.h> -#include <ppapi/c/ppb.h> -#include <pthread.h> #include <map> #include <string> -#include <vector> #include "nacl_io/kernel_object.h" -#include "nacl_io/mount.h" +#include "nacl_io/mount_factory.h" #include "nacl_io/ostypes.h" -#include "nacl_io/path.h" +#include "nacl_io/osutime.h" -class KernelHandle; -class Mount; -class MountNode; class PepperInterface; // KernelProxy provide one-to-one mapping for libc kernel calls. Calls to the @@ -29,9 +22,7 @@ class PepperInterface; // other classes should return Error (as defined by nacl_io/error.h). class KernelProxy : protected KernelObject { public: - typedef Error (*MountFactory_t)(int, StringMap_t&, PepperInterface*, Mount**); - typedef std::map<std::string, std::string> StringMap_t; - typedef std::map<std::string, MountFactory_t> MountFactoryMap_t; + typedef std::map<std::string, MountFactory*> MountFactoryMap_t; KernelProxy(); virtual ~KernelProxy(); @@ -40,7 +31,7 @@ class KernelProxy : protected KernelObject { virtual void Init(PepperInterface* ppapi); // KernelHandle and FD allocation and manipulation functions. - virtual int open(const char *path, int oflag); + virtual int open(const char* path, int oflag); virtual int close(int fd); virtual int dup(int fd); virtual int dup2(int fd, int newfd); @@ -53,6 +44,12 @@ class KernelProxy : protected KernelObject { const char *filesystemtype, unsigned long mountflags, const void *data); virtual int umount(const char *path); + // Stub system calls that don't do anything (yet), handled by KernelProxy. + virtual int chown(const char* path, uid_t owner, gid_t group); + virtual int fchown(int fd, uid_t owner, gid_t group); + virtual int lchown(const char* path, uid_t owner, gid_t group); + virtual int utime(const char* filename, const struct utimbuf* times); + // System calls that take a path as an argument: // The kernel proxy will look for the Node associated to the path. To // find the node, the kernel proxy calls the corresponding mount's GetNode() @@ -76,6 +73,7 @@ class KernelProxy : protected KernelObject { virtual int ftruncate(int fd, off_t length); virtual int fsync(int fd); virtual int isatty(int fd); + virtual int ioctl(int d, int request, char *argp); // lseek() relies on the mount's Stat() to determine whether or not the // file handle corresponding to fd is a directory @@ -93,7 +91,11 @@ class KernelProxy : protected KernelObject { virtual int link(const char* oldpath, const char* newpath); virtual int symlink(const char* oldpath, const char* newpath); - virtual void* mmap(void* addr, size_t length, int prot, int flags, int fd, + virtual void* mmap(void* addr, + size_t length, + int prot, + int flags, + int fd, size_t offset); virtual int munmap(void* addr, size_t length); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h b/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h index 322fe72154..ca1ada6af6 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h @@ -7,6 +7,8 @@ #include <sys/types.h> #include <stdlib.h> +#include "nacl_io/ostypes.h" +#include "nacl_io/osutime.h" #include "sdk_util/macros.h" #if defined(__GLIBC__) @@ -37,9 +39,11 @@ void kernel_wrap_init(); int NAME(access)(const char* path, int amode) NOTHROW; int NAME(chdir)(const char* path) NOTHROW; int NAME(chmod)(const char* path, chmod_mode_t mode) NOTHROW; +int chown(const char* path, uid_t owner, gid_t group); int NAME(close)(int fd); int NAME(dup)(int oldfd) NOTHROW; int NAME(dup2)(int oldfd, int newfd) NOTHROW; +int fchown(int fd, uid_t owner, gid_t group); #if defined(WIN32) int _fstat32(int fd, struct _stat32* buf); int _fstat64(int fd, struct _stat64* buf); @@ -54,7 +58,9 @@ int ftruncate(int fd, off_t length) NOTHROW; char* NAME(getcwd)(char* buf, getcwd_size_t size) NOTHROW; char* getwd(char* buf) NOTHROW; int getdents(int fd, void* buf, unsigned int count) NOTHROW; +int ioctl(int d, int request, char* argp) NOTHROW; int NAME(isatty)(int fd) NOTHROW; +int lchown(const char* path, uid_t owner, gid_t group); int link(const char* oldpath, const char* newpath) NOTHROW; off_t NAME(lseek)(int fd, off_t offset, int whence) NOTHROW; #if defined(WIN32) @@ -82,6 +88,7 @@ int stat(const char* path, struct stat* buf) NOTHROW; int symlink(const char* oldpath, const char* newpath) NOTHROW; int umount(const char* path) NOTHROW; int NAME(unlink)(const char* path) NOTHROW; +int utime(const char* filename, const struct utimbuf* times); read_ssize_t NAME(write)(int fd, const void* buf, size_t nbyte); EXTERN_C_END diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc index efa765b075..2de85f800c 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc @@ -138,6 +138,10 @@ int WRAP(chdir) (const char* pathname) { return (ki_chdir(pathname)) ? errno : 0; } +int chown(const char* path, uid_t owner, gid_t group) { + return ki_chown(path, owner, group); +} + int WRAP(close)(int fd) { return (ki_close(fd) < 0) ? errno : 0; } @@ -151,6 +155,10 @@ int WRAP(dup2)(int fd, int newfd) NOTHROW { return (ki_dup2(fd, newfd) < 0) ? errno : 0; } +int fchown(int fd, uid_t owner, gid_t group) { + return ki_fchown(fd, owner, group); +} + int WRAP(fstat)(int fd, struct nacl_abi_stat *nacl_buf) { struct stat buf; memset(&buf, 0, sizeof(struct stat)); @@ -215,10 +223,18 @@ int WRAP(getdents)(int fd, dirent* nacl_buf, size_t nacl_count, size_t *nread) { return 0; } +int ioctl(int d, int request, char* argp) NOTHROW { + return ki_ioctl(d, request, argp); +} + int isatty(int fd) NOTHROW { return ki_isatty(fd); } +int lchown(const char* path, uid_t owner, gid_t group) { + return ki_lchown(path, owner, group); +} + int link(const char* oldpath, const char* newpath) NOTHROW { return ki_link(oldpath, newpath); } @@ -310,7 +326,11 @@ int unlink(const char* path) NOTHROW { return ki_unlink(path); } -int WRAP(write)(int fd, const void *buf, size_t count, size_t *nwrote) { +int utime(const char* filename, const struct utimbuf* times) { + return ki_utime(filename, times); +} + +int WRAP(write)(int fd, const void* buf, size_t count, size_t* nwrote) { if (!ki_is_initialized()) return REAL(write)(fd, buf, count, nwrote); @@ -326,7 +346,7 @@ int _real_close(int fd) { return REAL(close)(fd); } -int _real_fstat(int fd, struct stat *buf) { +int _real_fstat(int fd, struct stat* buf) { struct nacl_abi_stat st; int err = REAL(fstat)(fd, &st); if (err) { @@ -338,7 +358,7 @@ int _real_fstat(int fd, struct stat *buf) { return 0; } -int _real_getdents(int fd, void* buf, size_t count, size_t *nread) { +int _real_getdents(int fd, void* buf, size_t count, size_t* nread) { // "buf" contains dirent(s); "nacl_buf" contains nacl_abi_dirent(s). // See WRAP(getdents) above. char* nacl_buf = (char*)alloca(count); @@ -434,3 +454,4 @@ EXTERN_C_END #endif // defined(__native_client__) && defined(__GLIBC__) + diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc index 722d54ef6f..d2f824253b 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc @@ -63,6 +63,10 @@ int chmod(const char* path, mode_t mode) { return ki_chmod(path, mode); } +int chown(const char* path, uid_t owner, gid_t group) { + return ki_chown(path, owner, group); +} + int WRAP(close)(int fd) { return (ki_close(fd) < 0) ? errno : 0; } @@ -77,7 +81,11 @@ int WRAP(dup2)(int fd, int newfd) { return (newfd < 0) ? errno : 0; } -int WRAP(fstat)(int fd, struct stat *buf) { +int fchown(int fd, uid_t owner, gid_t group) { + return ki_fchown(fd, owner, group); +} + +int WRAP(fstat)(int fd, struct stat* buf) { return (ki_fstat(fd, buf) < 0) ? errno : 0; } @@ -101,14 +109,22 @@ int getdents(int fd, void* buf, unsigned int count) { return ki_getdents(fd, buf, count); } -int WRAP(getdents)(int fd, dirent* buf, size_t count, size_t *nread) { +int WRAP(getdents)(int fd, dirent* buf, size_t count, size_t* nread) { return (ki_getdents(fd, buf, count) < 0) ? errno : 0; } +int ioctl(int d, int request, char* argp) { + return ki_ioctl(d, request, argp); +} + int isatty(int fd) { return ki_isatty(fd); } +int lchown(const char* path, uid_t owner, gid_t group) { + return ki_lchown(path, owner, group); +} + int link(const char* oldpath, const char* newpath) { return ki_link(oldpath, newpath); } @@ -143,7 +159,7 @@ int WRAP(open)(const char* pathname, int oflag, mode_t cmode, int* newfd) { return (*newfd < 0) ? errno : 0; } -int WRAP(read)(int fd, void *buf, size_t count, size_t *nread) { +int WRAP(read)(int fd, void* buf, size_t count, size_t* nread) { if (!ki_is_initialized()) return REAL(read)(fd, buf, count, nread); @@ -181,6 +197,10 @@ int unlink(const char* path) { return ki_unlink(path); } +int utime(const char *filename, const struct utimbuf* times) { + return ki_utime(filename, times); +} + int WRAP(write)(int fd, const void *buf, size_t count, size_t *nwrote) { if (!ki_is_initialized()) return REAL(write)(fd, buf, count, nwrote); @@ -268,3 +288,4 @@ EXTERN_C_END #endif // defined(__native_client__) && !defined(__GLIBC__) + diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_win.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_win.cc index 79dfa879b1..85bfd0cf06 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_win.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_win.cc @@ -55,6 +55,10 @@ int _chmod(const char* path, mode_t mode) { return ki_chmod(path, mode); } +int chown(const char* path, uid_t owner, gid_t group) { + return ki_chown(path, owner, group); +} + int _close(int fd) { return ki_close(fd); } @@ -71,6 +75,10 @@ int _dup2(int oldfd, int newfd) { return ki_dup2(oldfd, newfd); } +int fchown(int fd, uid_t owner, gid_t group) { + return ki_fchown(fd, owner, group); +} + int _fstat32(int fd, struct _stat32* buf) { struct stat ki_buf; int res = ki_fstat(fd, &ki_buf); @@ -119,10 +127,18 @@ int getdents(int fd, void* buf, unsigned int count) { return ki_getdents(fd, buf, count); } +int ioctl(int d, int request, char* argp) { + return ki_ioctl(d, request, argp); +} + int _isatty(int fd) { return ki_isatty(fd); } +int lchown(const char* path, uid_t owner, gid_t group) { + return ki_lchown(path, owner, group); +} + int link(const char* oldpath, const char* newpath) { return ki_link(oldpath, newpath); } @@ -226,6 +242,10 @@ int _unlink(const char* path) { return ki_unlink(path); } +int _utime(const char* filename, const struct utimbuf* times) { + return ki_utime(filename, times); +} + int _write(int fd, const void* buf, size_t nbyte) { if (!ki_is_initialized()) return 0; @@ -294,3 +314,4 @@ void kernel_wrap_init() { EXTERN_C_END #endif // defined(WIN32) + diff --git a/native_client_sdk/src/libraries/nacl_io/library.dsc b/native_client_sdk/src/libraries/nacl_io/library.dsc index 81d621cfa2..eef0ac62ee 100644 --- a/native_client_sdk/src/libraries/nacl_io/library.dsc +++ b/native_client_sdk/src/libraries/nacl_io/library.dsc @@ -39,6 +39,7 @@ 'FILES': [ "error.h", "inode_pool.h", + "ioctl.h", "kernel_handle.h", "kernel_intercept.h", "kernel_object.h", @@ -47,6 +48,7 @@ "kernel_wrap_real.h", "mount.h", "mount_dev.h", + "mount_factory.h", "mount_html5fs.h", "mount_http.h", "mount_mem.h", @@ -62,9 +64,12 @@ "osmman.h", "osstat.h", "ostypes.h", + "osunistd.h", + "osutime.h", "path.h", "pepper_interface.h", "real_pepper_interface.h", + "typed_mount_factory.h", ], 'DEST': 'include/nacl_io', }, diff --git a/native_client_sdk/src/libraries/nacl_io/mount.cc b/native_client_sdk/src/libraries/nacl_io/mount.cc index 76fe3a7848..d944ac96bd 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount.cc @@ -32,18 +32,8 @@ Error Mount::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { void Mount::Destroy() {} -void Mount::AcquireNode(MountNode* node) { - AutoLock lock(&lock_); - node->Acquire(); -} - -void Mount::ReleaseNode(MountNode* node) { - AutoLock lock(&lock_); - node->Release(); -} - -Error Mount::OpenResource(const Path& path, MountNode** out_node) { - *out_node = NULL; +Error Mount::OpenResource(const Path& path, ScopedMountNode* out_node) { + out_node->reset(NULL); return EINVAL; } @@ -69,3 +59,4 @@ void Mount::OnNodeDestroyed(MountNode* node) { if (node->stat_.st_ino) inode_pool_.Release(node->stat_.st_ino); } + diff --git a/native_client_sdk/src/libraries/nacl_io/mount.h b/native_client_sdk/src/libraries/nacl_io/mount.h index ddf1fd5c76..58cf1a4ed1 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.h +++ b/native_client_sdk/src/libraries/nacl_io/mount.h @@ -12,12 +12,16 @@ #include "nacl_io/inode_pool.h" #include "nacl_io/mount_node.h" #include "nacl_io/path.h" + #include "sdk_util/macros.h" #include "sdk_util/ref_object.h" +#include "sdk_util/scoped_ref.h" +class Mount; class MountNode; class PepperInterface; +typedef ScopedRef<Mount> ScopedMount; typedef std::map<std::string, std::string> StringMap_t; // NOTE: The KernelProxy is the only class that should be setting errno. All @@ -36,31 +40,25 @@ class Mount : public RefObject { virtual void Destroy(); public: - template <class M> - // Assumes that |out_mount| is non-NULL. - static Error Create(int dev, - StringMap_t& args, - PepperInterface* ppapi, - Mount** out_mount); - PepperInterface* ppapi() { return ppapi_; } - // Assumes that |node| is non-NULL. - void AcquireNode(MountNode* node); - // Assumes that |node| is non-NULL. - void ReleaseNode(MountNode* node); - // All paths in functions below are expected to containing a leading "/". + // Test whether a file or directory at a given path can be accessed. + // Returns 0 on success, or an appropriate errno value on failure. + virtual Error Access(const Path& path, int a_mode) = 0; + // Open a node at |path| with the specified open flags. The resulting // MountNode is created with a ref count of 1. // Assumes that |out_node| is non-NULL. - virtual Error Open(const Path& path, int o_flags, MountNode** out_node) = 0; + virtual Error Open(const Path& path, + int o_flags, + ScopedMountNode* out_node) = 0; // OpenResource is only used to read files from the NaCl NMF file. No mount // except MountPassthrough should implement it. // Assumes that |out_node| is non-NULL. - virtual Error OpenResource(const Path& path, MountNode** out_node); + virtual Error OpenResource(const Path& path, ScopedMountNode* out_node); // Unlink, Mkdir, Rmdir will affect the both the RefCount // and the nlink number in the stat object. @@ -74,6 +72,7 @@ class Mount : public RefObject { // Assumes that |node| is non-NULL. void OnNodeCreated(MountNode* node); + // Assumes that |node| is non-NULL. void OnNodeDestroyed(MountNode* node); @@ -88,28 +87,7 @@ class Mount : public RefObject { // lock is held, so we make it private. friend class KernelObject; friend class KernelProxy; - void Acquire() { RefObject::Acquire(); } - bool Release() { return RefObject::Release(); } - DISALLOW_COPY_AND_ASSIGN(Mount); }; -/*static*/ -template <class M> -Error Mount::Create(int dev, - StringMap_t& args, - PepperInterface* ppapi, - Mount** out_mount) { - Mount* mnt = new M(); - Error error = mnt->Init(dev, args, ppapi); - if (error) { - delete mnt; - *out_mount = NULL; - return error; - } - - *out_mount = mnt; - return 0; -} - #endif // LIBRARIES_NACL_IO_MOUNT_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc index 11893ea09e..78b5916fd5 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. +/* Copyright (c) 2013 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. */ @@ -8,11 +8,17 @@ #include <errno.h> #include <fcntl.h> +#include <pthread.h> #include <string.h> + +#include <deque> + +#include "nacl_io/ioctl.h" #include "nacl_io/kernel_wrap_real.h" #include "nacl_io/mount_dev.h" #include "nacl_io/mount_node.h" #include "nacl_io/mount_node_dir.h" +#include "nacl_io/osunistd.h" #include "nacl_io/pepper_interface.h" #include "sdk_util/auto_lock.h" @@ -66,11 +72,25 @@ class ConsoleNode : public NullNode { class TtyNode : public NullNode { public: explicit TtyNode(Mount* mount); + ~TtyNode(); + + virtual Error Ioctl(int request, + char* arg); + + virtual Error Read(size_t offs, + void* buf, + size_t count, + int* out_bytes); virtual Error Write(size_t offs, const void* buf, size_t count, int* out_bytes); + + private: + std::deque<char> input_buffer_; + pthread_cond_t is_readable_; + std::string prefix_; }; class ZeroNode : public MountNode { @@ -175,7 +195,14 @@ Error ConsoleNode::Write(size_t offs, return 0; } -TtyNode::TtyNode(Mount* mount) : NullNode(mount) {} +TtyNode::TtyNode(Mount* mount) : NullNode(mount) { + prefix_ = "_default_:"; + pthread_cond_init(&is_readable_, NULL); +} + +TtyNode::~TtyNode() { + pthread_cond_destroy(&is_readable_); +} Error TtyNode::Write(size_t offs, const void* buf, @@ -189,15 +216,68 @@ Error TtyNode::Write(size_t offs, if (!(var_intr && msg_intr)) return ENOSYS; + // We append the prefix_ to the data in buf, then package it up + // and post it as a message. const char* data = static_cast<const char*>(buf); - uint32_t len = static_cast<uint32_t>(count); - struct PP_Var val = var_intr->VarFromUtf8(data, len); + std::string message; + { + AutoLock lock(&lock_); + message = prefix_; + } + message.append(data, count); + uint32_t len = static_cast<uint32_t>(message.size()); + struct PP_Var val = var_intr->VarFromUtf8(message.data(), len); msg_intr->PostMessage(mount_->ppapi()->GetInstance(), val); - *out_bytes = count; return 0; } +Error TtyNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + AutoLock lock(&lock_); + while (input_buffer_.size() <= 0) { + pthread_cond_wait(&is_readable_, &lock_); + } + + // Copies data from the input buffer into buf. + size_t bytes_to_copy = std::min(count, input_buffer_.size()); + std::copy(input_buffer_.begin(), input_buffer_.begin() + bytes_to_copy, + static_cast<char*>(buf)); + *out_bytes = bytes_to_copy; + input_buffer_.erase(input_buffer_.begin(), + input_buffer_.begin() + bytes_to_copy); + return 0; +} + +Error TtyNode::Ioctl(int request, char* arg) { + if (request == TIOCNACLPREFIX) { + // This ioctl is used to change the prefix for this tty node. + // The prefix is used to distinguish messages intended for this + // tty node from all the other messages cluttering up the + // javascript postMessage() channel. + AutoLock lock(&lock_); + prefix_ = arg; + return 0; + } else if (request == TIOCNACLINPUT) { + // This ioctl is used to deliver data from the user to this tty node's + // input buffer. We check if the prefix in the input data matches the + // prefix for this node, and only deliver the data if so. + struct tioc_nacl_input_string* message = + reinterpret_cast<struct tioc_nacl_input_string*>(arg); + AutoLock lock(&lock_); + if (message->length >= prefix_.size() && + strncmp(message->buffer, prefix_.data(), prefix_.size()) == 0) { + input_buffer_.insert(input_buffer_.end(), + message->buffer + prefix_.size(), + message->buffer + message->length); + pthread_cond_broadcast(&is_readable_); + return 0; + } + return ENOTTY; + } else { + return EINVAL; + } +} + ZeroNode::ZeroNode(Mount* mount) : MountNode(mount) { stat_.st_mode = S_IFCHR; } Error ZeroNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { @@ -269,23 +349,28 @@ Error UrandomNode::Write(size_t offs, } // namespace -Error MountDev::Open(const Path& path, int mode, MountNode** out_node) { - *out_node = NULL; +Error MountDev::Access(const Path& path, int a_mode) { + ScopedMountNode node; + int error = root_->FindChild(path.Join(), &node); + if (error) + return error; + + // Don't allow execute access. + if (a_mode & X_OK) + return EACCES; + return 0; +} + +Error MountDev::Open(const Path& path, int mode, ScopedMountNode* out_node) { + out_node->reset(NULL); AutoLock lock(&lock_); // Don't allow creating any files. if (mode & O_CREAT) return EINVAL; - MountNode* node = NULL; - int error = root_->FindChild(path.Join(), &node); - if (error) - return error; - - node->Acquire(); - *out_node = node; - return 0; + return root_->FindChild(path.Join(), out_node); } Error MountDev::Unlink(const Path& path) { return EINVAL; } @@ -298,14 +383,14 @@ Error MountDev::Remove(const Path& path) { return EINVAL; } MountDev::MountDev() {} -#define INITIALIZE_DEV_NODE(path, klass) \ - error = root_->AddChild(path, new klass(this)); \ - if (error) \ +#define INITIALIZE_DEV_NODE(path, klass) \ + error = root_->AddChild(path, ScopedMountNode(new klass(this))); \ + if (error) \ return error; -#define INITIALIZE_DEV_NODE_1(path, klass, arg) \ - error = root_->AddChild(path, new klass(this, arg)); \ - if (error) \ +#define INITIALIZE_DEV_NODE_1(path, klass, arg) \ + error = root_->AddChild(path, ScopedMountNode(new klass(this, arg))); \ + if (error) \ return error; Error MountDev::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { @@ -313,7 +398,7 @@ Error MountDev::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { if (error) return error; - root_ = new MountNodeDir(this); + root_.reset(new MountNodeDir(this)); INITIALIZE_DEV_NODE("/null", NullNode); INITIALIZE_DEV_NODE("/zero", ZeroNode); @@ -330,8 +415,3 @@ Error MountDev::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { return 0; } -void MountDev::Destroy() { - if (root_) - root_->Release(); - root_ = NULL; -} diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.h b/native_client_sdk/src/libraries/nacl_io/mount_dev.h index 7ad7daa3b9..5d47838b62 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.h @@ -6,13 +6,14 @@ #define LIBRARIES_NACL_IO_MOUNT_DEV_H_ #include "nacl_io/mount.h" +#include "nacl_io/typed_mount_factory.h" class MountNode; class MountDev : public Mount { public: - virtual Error Open(const Path& path, int mode, MountNode** out_node); - + virtual Error Access(const Path& path, int a_mode); + virtual Error Open(const Path& path, int mode, ScopedMountNode* out_node); virtual Error Unlink(const Path& path); virtual Error Mkdir(const Path& path, int permissions); virtual Error Rmdir(const Path& path); @@ -22,12 +23,12 @@ class MountDev : public Mount { MountDev(); virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); - virtual void Destroy(); private: - MountNode* root_; + ScopedMountNode root_; - friend class Mount; + friend class TypedMountFactory<MountDev>; + DISALLOW_COPY_AND_ASSIGN(MountDev); }; #endif // LIBRARIES_NACL_IO_MOUNT_DEV_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/mount_factory.h b/native_client_sdk/src/libraries/nacl_io/mount_factory.h new file mode 100644 index 0000000000..35f77d7609 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_factory.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2013 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. + */ +#ifndef LIBRARIES_NACL_IO_MOUNT_FACTORY_H_ +#define LIBRARIES_NACL_IO_MOUNT_FACTORY_H_ + +#include <map> +#include <string> + +#include "nacl_io/error.h" +#include "sdk_util/scoped_ref.h" + +class PepperInterface; +class Mount; + +typedef std::map<std::string, std::string> StringMap_t; + +class MountFactory { + public: + virtual ~MountFactory() {} + virtual Error CreateMount(int dev, + StringMap_t& args, + PepperInterface* ppapi, + ScopedRef<Mount>* out_mount) = 0; +}; + +#endif // LIBRARIES_NACL_IO_MOUNT_FACTORY_H_ + diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc index 0008284253..edac8f4c05 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc @@ -6,6 +6,7 @@ #include "nacl_io/mount_html5fs.h" #include <errno.h> +#include <fcntl.h> #include <ppapi/c/pp_completion_callback.h> #include <ppapi/c/pp_errors.h> #include <stdlib.h> @@ -24,9 +25,21 @@ int64_t strtoull(const char* nptr, char** endptr, int base) { } // namespace -Error MountHtml5Fs::Open(const Path& path, int mode, MountNode** out_node) { - *out_node = NULL; +Error MountHtml5Fs::Access(const Path& path, int a_mode) { + // a_mode is unused, since all files are readable, writable and executable. + ScopedMountNode node; + Error error = Open(path, O_RDONLY, &node); + if (error) + return error; + + node->Release(); + return 0; +} +Error MountHtml5Fs::Open(const Path& path, + int mode, + ScopedMountNode* out_node) { + out_node->reset(NULL); Error error = BlockUntilFilesystemOpen(); if (error) return error; @@ -34,14 +47,12 @@ Error MountHtml5Fs::Open(const Path& path, int mode, MountNode** out_node) { PP_Resource fileref = ppapi()->GetFileRefInterface() ->Create(filesystem_resource_, path.Join().c_str()); if (!fileref) - return ENOSYS; + return ENOENT; - MountNodeHtml5Fs* node = new MountNodeHtml5Fs(this, fileref); + ScopedMountNode node(new MountNodeHtml5Fs(this, fileref)); error = node->Init(mode); - if (error) { - node->Release(); + if (error) return error; - } *out_node = node; return 0; @@ -59,7 +70,7 @@ Error MountHtml5Fs::Mkdir(const Path& path, int permissions) { ppapi()->GetFileRefInterface()->Create(filesystem_resource_, path.Join().c_str())); if (!fileref_resource.pp_resource()) - return EIO; + return ENOENT; int32_t result = ppapi()->GetFileRefInterface()->MakeDirectory( fileref_resource.pp_resource(), PP_FALSE, PP_BlockUntilComplete()); @@ -81,7 +92,7 @@ Error MountHtml5Fs::Remove(const Path& path) { ppapi()->GetFileRefInterface()->Create(filesystem_resource_, path.Join().c_str())); if (!fileref_resource.pp_resource()) - return ENOSYS; + return ENOENT; int32_t result = ppapi()->GetFileRefInterface() ->Delete(fileref_resource.pp_resource(), PP_BlockUntilComplete()); @@ -178,3 +189,4 @@ void MountHtml5Fs::FilesystemOpenCallback(int32_t result) { filesystem_open_error_ = PPErrorToErrno(result); pthread_cond_signal(&filesystem_open_cond_); } + diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h index e872dae353..d2e1b083ee 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h @@ -6,14 +6,17 @@ #define LIBRARIES_NACL_IO_MOUNT_HTML5FS_H_ #include <pthread.h> + #include "nacl_io/mount.h" #include "nacl_io/pepper_interface.h" +#include "nacl_io/typed_mount_factory.h" class MountNode; class MountHtml5Fs : public Mount { public: - virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error Access(const Path& path, int a_mode); + virtual Error Open(const Path& path, int mode, ScopedMountNode* out_node); virtual Error Unlink(const Path& path); virtual Error Mkdir(const Path& path, int permissions); virtual Error Rmdir(const Path& path); @@ -38,7 +41,7 @@ class MountHtml5Fs : public Mount { Error filesystem_open_error_; // protected by lock_. pthread_cond_t filesystem_open_cond_; - friend class Mount; + friend class TypedMountFactory<MountHtml5Fs>; }; #endif // LIBRARIES_NACL_IO_MOUNT_HTML5FS_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_http.cc index 15c32e6231..9638746b03 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.cc @@ -21,7 +21,7 @@ #include "nacl_io/mount_node_dir.h" #include "nacl_io/mount_node_http.h" #include "nacl_io/osinttypes.h" -#include "sdk_util/auto_lock.h" +#include "nacl_io/osunistd.h" namespace { @@ -54,9 +54,32 @@ std::string NormalizeHeaderKey(const std::string& s) { return result; } -Error MountHttp::Open(const Path& path, int mode, MountNode** out_node) { - *out_node = NULL; +Error MountHttp::Access(const Path& path, int a_mode) { + assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); + + NodeMap_t::iterator iter = node_cache_.find(path.Join()); + if (iter == node_cache_.end()) { + // If we can't find the node in the cache, fetch it + std::string url = MakeUrl(path); + ScopedMountNode node(new MountNodeHttp(this, url, cache_content_)); + Error error = node->Init(O_RDONLY); + if (error) + return error; + + error = node->GetStat(NULL); + if (error) + return error; + } + + // Don't allow write or execute access. + if (a_mode & (W_OK | X_OK)) + return EACCES; + + return 0; +} +Error MountHttp::Open(const Path& path, int mode, ScopedMountNode* out_node) { + out_node->reset(NULL); assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); NodeMap_t::iterator iter = node_cache_.find(path.Join()); @@ -66,37 +89,26 @@ Error MountHttp::Open(const Path& path, int mode, MountNode** out_node) { } // If we can't find the node in the cache, create it - std::string url = url_root_ + (path.IsAbsolute() ? path.Range(1, path.Size()) - : path.Join()); - - MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); + std::string url = MakeUrl(path); + ScopedMountNode node(new MountNodeHttp(this, url, cache_content_)); Error error = node->Init(mode); - if (error) { - node->Release(); + if (error) return error; - } error = node->GetStat(NULL); - if (error) { - node->Release(); + if (error) return error; - } - MountNodeDir* parent; + ScopedMountNode parent; error = FindOrCreateDir(path.Parent(), &parent); - if (error) { - node->Release(); + if (error) return error; - } error = parent->AddChild(path.Basename(), node); - if (error) { - node->Release(); + if (error) return error; - } node_cache_[path.Join()] = node; - *out_node = node; return 0; } @@ -241,43 +253,36 @@ Error MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { void MountHttp::Destroy() {} -Error MountHttp::FindOrCreateDir(const Path& path, MountNodeDir** out_node) { - *out_node = NULL; - +Error MountHttp::FindOrCreateDir(const Path& path, + ScopedMountNode* out_node) { + out_node->reset(NULL); std::string strpath = path.Join(); NodeMap_t::iterator iter = node_cache_.find(strpath); if (iter != node_cache_.end()) { - *out_node = static_cast<MountNodeDir*>(iter->second); + *out_node = iter->second; return 0; } // If the node does not exist, create it. - MountNodeDir* node = new MountNodeDir(this); + ScopedMountNode node(new MountNodeDir(this)); Error error = node->Init(S_IREAD); - if (error) { - node->Release(); + if (error) return error; - } // If not the root node, find the parent node and add it to the parent if (!path.Top()) { - MountNodeDir* parent; + ScopedMountNode parent; error = FindOrCreateDir(path.Parent(), &parent); - if (error) { - node->Release(); + if (error) return error; - } error = parent->AddChild(path.Basename(), node); - if (error) { - node->Release(); + if (error) return error; - } } // Add it to the node cache. node_cache_[strpath] = node; - *out_node = node; return 0; } @@ -337,31 +342,24 @@ Error MountHttp::ParseManifest(char* text) { } Path path(name); - std::string url = - url_root_ + - (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join()); + std::string url = MakeUrl(path); + + MountNodeHttp* http_node = new MountNodeHttp(this, url, cache_content_); + ScopedMountNode node(http_node); - MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); Error error = node->Init(mode); - if (error) { - node->Release(); + if (error) return error; - } + http_node->SetCachedSize(atoi(lenstr)); - node->SetCachedSize(atoi(lenstr)); - - MountNodeDir* dir_node; + ScopedMountNode dir_node; error = FindOrCreateDir(path.Parent(), &dir_node); - if (error) { - node->Release(); + if (error) return error; - } error = dir_node->AddChild(path.Basename(), node); - if (error) { - node->Release(); + if (error) return error; - } std::string pname = path.Join(); node_cache_[pname] = node; @@ -374,7 +372,7 @@ Error MountHttp::ParseManifest(char* text) { Error MountHttp::LoadManifest(const std::string& manifest_name, char** out_manifest) { Path manifest_path(manifest_name); - MountNode* manifest_node = NULL; + ScopedMountNode manifest_node; *out_manifest = NULL; int error = Open(manifest_path, O_RDONLY, &manifest_node); @@ -383,22 +381,21 @@ Error MountHttp::LoadManifest(const std::string& manifest_name, size_t size; error = manifest_node->GetSize(&size); - if (error) { - manifest_node->Release(); + if (error) return error; - } char* text = new char[size + 1]; int len; error = manifest_node->Read(0, text, size, &len); - if (error) { - manifest_node->Release(); + if (error) return error; - } - manifest_node->Release(); text[len] = 0; - *out_manifest = text; return 0; } + +std::string MountHttp::MakeUrl(const Path& path) { + return url_root_ + + (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join()); +} diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.h b/native_client_sdk/src/libraries/nacl_io/mount_http.h index e10f1afcfe..e804f37d5a 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.h @@ -9,19 +9,19 @@ #include <string> #include "nacl_io/mount.h" #include "nacl_io/pepper_interface.h" +#include "nacl_io/typed_mount_factory.h" class MountNode; -class MountNodeDir; -class MountNodeHttp; class MountHttpMock; std::string NormalizeHeaderKey(const std::string& s); class MountHttp : public Mount { public: - typedef std::map<std::string, MountNode*> NodeMap_t; + typedef std::map<std::string, ScopedMountNode> NodeMap_t; - virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error Access(const Path& path, int a_mode); + virtual Error Open(const Path& path, int mode, ScopedMountNode* out_node); virtual Error Unlink(const Path& path); virtual Error Mkdir(const Path& path, int permissions); virtual Error Rmdir(const Path& path); @@ -36,11 +36,15 @@ class MountHttp : public Mount { virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); - Error FindOrCreateDir(const Path& path, MountNodeDir** out_node); + Error FindOrCreateDir(const Path& path, ScopedMountNode* out_node); Error LoadManifest(const std::string& path, char** out_manifest); Error ParseManifest(char *text); private: + // Gets the URL to fetch for |path|. + // |path| is relative to the mount point for the HTTP filesystem. + std::string MakeUrl(const Path& path); + std::string url_root_; StringMap_t headers_; NodeMap_t node_cache_; @@ -49,7 +53,7 @@ class MountHttp : public Mount { bool cache_stat_; bool cache_content_; - friend class Mount; + friend class TypedMountFactory<MountHttp>; friend class MountNodeHttp; friend class MountHttpMock; }; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc index 356600b4ec..41e0fddef9 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. +/* Copyright (c) 2012 The hromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -6,6 +6,7 @@ #include <errno.h> #include <fcntl.h> + #include <string> #include "nacl_io/mount.h" @@ -13,35 +14,32 @@ #include "nacl_io/mount_node_dir.h" #include "nacl_io/mount_node_mem.h" #include "nacl_io/osstat.h" +#include "nacl_io/osunistd.h" #include "nacl_io/path.h" #include "sdk_util/auto_lock.h" #include "sdk_util/ref_object.h" -MountMem::MountMem() : root_(NULL), max_ino_(0) {} +MountMem::MountMem() : root_(NULL) {} Error MountMem::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { Error error = Mount::Init(dev, args, ppapi); if (error) return error; - root_ = new MountNodeDir(this); + root_.reset(new MountNodeDir(this)); error = root_->Init(S_IREAD | S_IWRITE); if (error) { - root_->Release(); + root_.reset(NULL); return error; } - return 0; } -void MountMem::Destroy() { - if (root_) - root_->Release(); - root_ = NULL; -} - -Error MountMem::FindNode(const Path& path, int type, MountNode** out_node) { - MountNode* node = root_; +Error MountMem::FindNode(const Path& path, + int type, + ScopedMountNode* out_node) { + out_node->reset(NULL); + ScopedMountNode node = root_; // If there is no root there, we have an error. if (node == NULL) @@ -76,38 +74,47 @@ Error MountMem::FindNode(const Path& path, int type, MountNode** out_node) { return 0; } -Error MountMem::Open(const Path& path, int mode, MountNode** out_node) { - AutoLock lock(&lock_); - MountNode* node = NULL; - *out_node = NULL; - +Error MountMem::Access(const Path& path, int a_mode) { + ScopedMountNode node; Error error = FindNode(path, 0, &node); + if (error) + return error; + + int obj_mode = node->GetMode(); + if (((a_mode & R_OK) && !(obj_mode & S_IREAD)) || + ((a_mode & W_OK) && !(obj_mode & S_IWRITE)) || + ((a_mode & X_OK) && !(obj_mode & S_IEXEC))) { + return EACCES; + } + + return 0; +} + +Error MountMem::Open(const Path& path, int mode, ScopedMountNode* out_node) { + out_node->reset(NULL); + ScopedMountNode node; + + Error error = FindNode(path, 0, &node); if (error) { // If the node does not exist and we can't create it, fail if ((mode & O_CREAT) == 0) return ENOENT; // Now first find the parent directory to see if we can add it - MountNode* parent = NULL; + ScopedMountNode parent; error = FindNode(path.Parent(), S_IFDIR, &parent); if (error) return error; - // Create it with a single reference - node = new MountNodeMem(this); + node.reset(new MountNodeMem(this)); error = node->Init(OpenModeToPermission(mode)); - if (error) { - node->Release(); + if (error) return error; - } error = parent->AddChild(path.Basename(), node); - if (error) { - // Or if it fails, release it - node->Release(); + if (error) return error; - } *out_node = node; return 0; @@ -127,15 +134,11 @@ Error MountMem::Open(const Path& path, int mode, MountNode** out_node) { if ((obj_mode & req_mode) != req_mode) return EACCES; - // We opened it, so ref count it before passing it back. - node->Acquire(); *out_node = node; return 0; } Error MountMem::Mkdir(const Path& path, int mode) { - AutoLock lock(&lock_); - // We expect a Mount "absolute" path if (!path.IsAbsolute()) return ENOENT; @@ -144,12 +147,12 @@ Error MountMem::Mkdir(const Path& path, int mode) { if (path.Size() == 1) return EEXIST; - MountNode* parent = NULL; + ScopedMountNode parent; int error = FindNode(path.Parent(), S_IFDIR, &parent); if (error) return error; - MountNode* node = NULL; + ScopedMountNode node; error = parent->FindChild(path.Basename(), &node); if (!error) return EEXIST; @@ -160,21 +163,12 @@ Error MountMem::Mkdir(const Path& path, int mode) { // Allocate a node, with a RefCount of 1. If added to the parent // it will get ref counted again. In either case, release the // recount we have on exit. - node = new MountNodeDir(this); + node.reset(new MountNodeDir(this)); error = node->Init(S_IREAD | S_IWRITE); - if (error) { - node->Release(); - return error; - } - - error = parent->AddChild(path.Basename(), node); - if (error) { - node->Release(); + if (error) return error; - } - node->Release(); - return 0; + return parent->AddChild(path.Basename(), node); } Error MountMem::Unlink(const Path& path) { @@ -190,7 +184,6 @@ Error MountMem::Remove(const Path& path) { } Error MountMem::RemoveInternal(const Path& path, int remove_type) { - AutoLock lock(&lock_); bool dir_only = remove_type == REMOVE_DIR; bool file_only = remove_type == REMOVE_FILE; bool remove_dir = (remove_type & REMOVE_DIR) != 0; @@ -205,13 +198,13 @@ Error MountMem::RemoveInternal(const Path& path, int remove_type) { return EEXIST; } - MountNode* parent = NULL; + ScopedMountNode parent; int error = FindNode(path.Parent(), S_IFDIR, &parent); if (error) return error; // Verify we find a child which is a directory. - MountNode* child = NULL; + ScopedMountNode child; error = parent->FindChild(path.Basename(), &child); if (error) return error; @@ -227,3 +220,4 @@ Error MountMem::RemoveInternal(const Path& path, int remove_type) { return parent->RemoveChild(path.Basename()); } + diff --git a/native_client_sdk/src/libraries/nacl_io/mount_mem.h b/native_client_sdk/src/libraries/nacl_io/mount_mem.h index 930e9c734a..9bd679dba5 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_mem.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.h @@ -5,17 +5,14 @@ #ifndef LIBRARIES_NACL_IO_MOUNT_MEM_H_ #define LIBRARIES_NACL_IO_MOUNT_MEM_H_ -#include <map> -#include <string> - #include "nacl_io/mount.h" +#include "nacl_io/typed_mount_factory.h" class MountMem : public Mount { protected: MountMem(); virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); - virtual void Destroy(); // The protected functions are only used internally and will not // acquire or release the mount's lock themselves. The caller is @@ -26,10 +23,11 @@ class MountMem : public Mount { void FreeINO(int ino); // Find a Node specified node optionally failing if type does not match. - virtual Error FindNode(const Path& path, int type, MountNode** out_node); + virtual Error FindNode(const Path& path, int type, ScopedMountNode* out_node); public: - virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error Access(const Path& path, int a_mode); + virtual Error Open(const Path& path, int mode, ScopedMountNode* out_node); virtual Error Unlink(const Path& path); virtual Error Mkdir(const Path& path, int perm); virtual Error Rmdir(const Path& path); @@ -42,10 +40,9 @@ private: Error RemoveInternal(const Path& path, int remove_type); - MountNode* root_; - size_t max_ino_; + ScopedMountNode root_; - friend class Mount; + friend class TypedMountFactory<MountMem>; DISALLOW_COPY_AND_ASSIGN(MountMem); }; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.cc b/native_client_sdk/src/libraries/nacl_io/mount_node.cc index 34de7a8164..424df70e7c 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.cc @@ -8,6 +8,8 @@ #include <fcntl.h> #include <string.h> #include <sys/stat.h> + +#include <algorithm> #include <string> #include "nacl_io/kernel_wrap_real.h" @@ -46,9 +48,7 @@ void MountNode::Destroy() { Error MountNode::FSync() { return 0; } -Error MountNode::FTruncate(off_t length) { - return EINVAL; -} +Error MountNode::FTruncate(off_t length) { return EINVAL; } Error MountNode::GetDents(size_t offs, struct dirent* pdir, @@ -64,9 +64,7 @@ Error MountNode::GetStat(struct stat* pstat) { return 0; } -Error MountNode::Ioctl(int request, char* arg) { - return EINVAL; -} +Error MountNode::Ioctl(int request, char* arg) { return EINVAL; } Error MountNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { *out_bytes = 0; @@ -133,29 +131,21 @@ bool MountNode::IsaFile() { return (stat_.st_mode & S_IFREG) != 0; } bool MountNode::IsaTTY() { return (stat_.st_mode & S_IFCHR) != 0; } -Error MountNode::AddChild(const std::string& name, MountNode* node) { +Error MountNode::AddChild(const std::string& name, + const ScopedMountNode& node) { return ENOTDIR; } -Error MountNode::RemoveChild(const std::string& name) { - return ENOTDIR; -} +Error MountNode::RemoveChild(const std::string& name) { return ENOTDIR; } -Error MountNode::FindChild(const std::string& name, MountNode** out_node) { - *out_node = NULL; +Error MountNode::FindChild(const std::string& name, ScopedMountNode* out_node) { + out_node->reset(NULL); return ENOTDIR; } -int MountNode::ChildCount() { - return 0; -} +int MountNode::ChildCount() { return 0; } -void MountNode::Link() { - Acquire(); - stat_.st_nlink++; -} +void MountNode::Link() { stat_.st_nlink++; } + +void MountNode::Unlink() { stat_.st_nlink--; } -void MountNode::Unlink() { - stat_.st_nlink--; - Release(); -} diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.h b/native_client_sdk/src/libraries/nacl_io/mount_node.h index 0acce66f80..fb0f14f2c5 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.h @@ -9,11 +9,17 @@ #include "nacl_io/error.h" #include "nacl_io/osstat.h" + #include "sdk_util/ref_object.h" +#include "sdk_util/scoped_ref.h" struct dirent; struct stat; + class Mount; +class MountNode; + +typedef ScopedRef<MountNode> ScopedMountNode; // NOTE: The KernelProxy is the only class that should be setting errno. All // other classes should return Error (as defined by nacl_io/error.h). @@ -63,24 +69,26 @@ class MountNode : public RefObject { virtual int GetMode(); virtual int GetType(); // Assume that |out_size| is non-NULL. - virtual Error GetSize(size_t *out_size); + virtual Error GetSize(size_t* out_size); virtual bool IsaDir(); virtual bool IsaFile(); virtual bool IsaTTY(); + // Number of children for this node (directory) + virtual int ChildCount(); + protected: // Directory operations on the node are done by the Mount. The mount's lock // must be held while these calls are made. // Adds or removes a directory entry updating the link numbers and refcount // Assumes that |node| is non-NULL. - virtual Error AddChild(const std::string& name, MountNode* node); + virtual Error AddChild(const std::string& name, const ScopedMountNode& node); virtual Error RemoveChild(const std::string& name); // Find a child and return it without updating the refcount // Assumes that |out_node| is non-NULL. - virtual Error FindChild(const std::string& name, MountNode** out_node); - virtual int ChildCount(); + virtual Error FindChild(const std::string& name, ScopedMountNode* out_node); // Update the link count virtual void Link(); @@ -88,6 +96,11 @@ class MountNode : public RefObject { protected: struct stat stat_; + + // We use a pointer directly to avoid cycles in the ref count. + // TODO(noelallen) We should change this so it's unnecessary for the node + // to track it's parent. When a node is unlinked, the mount should do + // any cleanup it needs. Mount* mount_; friend class Mount; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc index 6f7feab44a..5aa39f966c 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc @@ -73,7 +73,8 @@ Error MountNodeDir::GetDents(size_t offs, return 0; } -Error MountNodeDir::AddChild(const std::string& name, MountNode* node) { +Error MountNodeDir::AddChild(const std::string& name, + const ScopedMountNode& node) { AutoLock lock(&lock_); if (name.empty()) @@ -104,8 +105,9 @@ Error MountNodeDir::RemoveChild(const std::string& name) { return ENOENT; } -Error MountNodeDir::FindChild(const std::string& name, MountNode** out_node) { - *out_node = NULL; +Error MountNodeDir::FindChild(const std::string& name, + ScopedMountNode* out_node) { + out_node->reset(NULL); AutoLock lock(&lock_); MountNodeMap_t::iterator it = map_.find(name); @@ -131,9 +133,8 @@ void MountNodeDir::BuildCache() { cache_ = (struct dirent*)malloc(sizeof(struct dirent) * map_.size()); MountNodeMap_t::iterator it = map_.begin(); for (size_t index = 0; it != map_.end(); it++, index++) { - MountNode* node = it->second; size_t len = it->first.length(); - cache_[index].d_ino = node->stat_.st_ino; + cache_[index].d_ino = it->second->stat_.st_ino; cache_[index].d_off = sizeof(struct dirent); cache_[index].d_reclen = sizeof(struct dirent); cache_[index].d_name[len] = 0; @@ -141,3 +142,4 @@ void MountNodeDir::BuildCache() { } } } + diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h index a2ab67ecfa..dd635fe50b 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h @@ -16,6 +16,9 @@ class MountDev; class MountHtml5Fs; class MountHttp; class MountMem; +class MountNodeDir; + +typedef ScopedRef<MountNodeDir> ScopedMountNodeDir; class MountNodeDir : public MountNode { protected: @@ -23,7 +26,7 @@ class MountNodeDir : public MountNode { virtual ~MountNodeDir(); public: - typedef std::map<std::string, MountNode*> MountNodeMap_t; + typedef std::map<std::string, ScopedMountNode> MountNodeMap_t; virtual Error FTruncate(off_t size); virtual Error GetDents(size_t offs, @@ -34,9 +37,9 @@ class MountNodeDir : public MountNode { virtual Error Write(size_t offs, void *buf, size_t count, int* out_bytes); // Adds a finds or adds a directory entry as an INO, updating the refcount - virtual Error AddChild(const std::string& name, MountNode *node); + virtual Error AddChild(const std::string& name, const ScopedMountNode& node); virtual Error RemoveChild(const std::string& name); - virtual Error FindChild(const std::string& name, MountNode** out_node); + virtual Error FindChild(const std::string& name, ScopedMountNode* out_node); virtual int ChildCount(); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc index 8be2a4f74b..a6bfc9b12b 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc @@ -268,10 +268,12 @@ Error MountNodeHtml5Fs::Init(int perm) { // First query the FileRef to see if it is a file or directory. PP_FileInfo file_info; - mount_->ppapi()->GetFileRefInterface()->Query(fileref_resource_, &file_info, - PP_BlockUntilComplete()); + int32_t query_result = + mount_->ppapi()->GetFileRefInterface()->Query(fileref_resource_, + &file_info, + PP_BlockUntilComplete()); // If this is a directory, do not get a FileIO. - if (file_info.type == PP_FILETYPE_DIRECTORY) + if (query_result == PP_OK && file_info.type == PP_FILETYPE_DIRECTORY) return 0; fileio_resource_ = mount_->ppapi()->GetFileIoInterface() @@ -302,3 +304,4 @@ void MountNodeHtml5Fs::Destroy() { fileref_resource_ = 0; MountNode::Destroy(); } + diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc index 5005ff48d3..02303953ae 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc @@ -27,6 +27,8 @@ namespace { const size_t MAX_READ_BUFFER_SIZE = 64 * 1024; const int32_t STATUSCODE_OK = 200; const int32_t STATUSCODE_PARTIAL_CONTENT = 206; +const int32_t STATUSCODE_FORBIDDEN = 403; +const int32_t STATUSCODE_NOT_FOUND = 404; StringMap_t ParseHeaders(const char* headers, int32_t headers_length) { enum State { @@ -124,6 +126,22 @@ bool ParseContentRange(const StringMap_t& headers, return false; } +// Maps an HTTP |status_code| onto the appropriate errno code. +int HTTPStatusCodeToErrno(int status_code) { + switch (status_code) { + case STATUSCODE_OK: + case STATUSCODE_PARTIAL_CONTENT: + return 0; + case STATUSCODE_FORBIDDEN: + return EACCES; + case STATUSCODE_NOT_FOUND: + return ENOENT; + } + if (status_code >= 400 && status_code < 500) + return EINVAL; + return EIO; +} + } // namespace void MountNodeHttp::SetCachedSize(off_t size) { @@ -298,10 +316,9 @@ Error MountNodeHttp::OpenUrl(const char* method, *out_statuscode = statuscode.value.as_int; // Only accept OK or Partial Content. - if (*out_statuscode != STATUSCODE_OK && - *out_statuscode != STATUSCODE_PARTIAL_CONTENT) { - return EINVAL; - } + Error error = HTTPStatusCodeToErrno(*out_statuscode); + if (error) + return error; // Get response headers. PP_Var response_headers_var = response_interface->GetProperty( @@ -521,3 +538,4 @@ Error MountNodeHttp::DownloadToBuffer(PP_Resource loader, *out_bytes = count; return 0; } + diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc index d324079700..fb403d5cb3 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc @@ -43,7 +43,6 @@ Error MountNodeMem::Write(size_t offs, size_t count, int* out_bytes) { *out_bytes = 0; - AutoLock lock(&lock_); if (count == 0) @@ -77,7 +76,7 @@ Error MountNodeMem::FTruncate(off_t new_size) { if (newdata != NULL) { // Zero out new space. if (new_size > old_size) - memset(newdata + old_size, 0, new_size - old_size); + memset(newdata + old_size, 0, need - old_size); data_ = newdata; capacity_ = need; @@ -93,3 +92,4 @@ Error MountNodeMem::FTruncate(off_t new_size) { stat_.st_size = static_cast<off_t>(new_size); return EIO; } + diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc index 268cbea7a5..32cde000b2 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc @@ -108,29 +108,33 @@ Error MountPassthrough::Init(int dev, void MountPassthrough::Destroy() {} -Error MountPassthrough::Open(const Path& path, int mode, MountNode** out_node) { - *out_node = NULL; +Error MountPassthrough::Access(const Path& path, int a_mode) { + // There is no underlying 'access' syscall in NaCl. It just returns ENOSYS. + return ENOSYS; +} +Error MountPassthrough::Open(const Path& path, + int mode, + ScopedMountNode* out_node) { + out_node->reset(NULL); int real_fd; int error = _real_open(path.Join().c_str(), mode, 0666, &real_fd); if (error) return error; - MountNodePassthrough* node = new MountNodePassthrough(this, real_fd); - *out_node = node; + out_node->reset(new MountNodePassthrough(this, real_fd)); return 0; } -Error MountPassthrough::OpenResource(const Path& path, MountNode** out_node) { - *out_node = NULL; - +Error MountPassthrough::OpenResource(const Path& path, + ScopedMountNode* out_node) { int real_fd; + out_node->reset(NULL); int error = _real_open_resource(path.Join().c_str(), &real_fd); if (error) return error; - MountNodePassthrough* node = new MountNodePassthrough(this, real_fd); - *out_node = node; + out_node->reset(new MountNodePassthrough(this, real_fd)); return 0; } @@ -151,3 +155,4 @@ Error MountPassthrough::Remove(const Path& path) { // Not implemented by NaCl. return ENOSYS; } + diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h index b143abf007..4d441a0ba7 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h @@ -6,6 +6,7 @@ #define LIBRARIES_NACL_IO_MOUNT_PASSTHROUGH_H_ #include "nacl_io/mount.h" +#include "nacl_io/typed_mount_factory.h" class MountPassthrough : public Mount { protected: @@ -15,16 +16,17 @@ class MountPassthrough : public Mount { virtual void Destroy(); public: - virtual Error Open(const Path& path, int mode, MountNode** out_node); - virtual Error OpenResource(const Path& path, MountNode** out_node); + virtual Error Access(const Path& path, int a_mode); + virtual Error Open(const Path& path, int mode, ScopedMountNode* out_node); + virtual Error OpenResource(const Path& path, ScopedMountNode* out_node); virtual Error Unlink(const Path& path); virtual Error Mkdir(const Path& path, int perm); virtual Error Rmdir(const Path& path); virtual Error Remove(const Path& path); private: - friend class Mount; - DISALLOW_COPY_AND_ASSIGN(MountPassthrough); + friend class TypedMountFactory<MountPassthrough>; + DISALLOW_COPY_AND_ASSIGN(MountPassthrough); }; #endif // LIBRARIES_NACL_IO_MOUNT_PASSTHROUGH_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/nacl_io.cc b/native_client_sdk/src/libraries/nacl_io/nacl_io.cc index 9750cd858d..29371c9561 100644 --- a/native_client_sdk/src/libraries/nacl_io/nacl_io.cc +++ b/native_client_sdk/src/libraries/nacl_io/nacl_io.cc @@ -16,3 +16,4 @@ void nacl_io_init_ppapi(PP_Instance instance, PPB_GetInterface get_interface) { ki_init_ppapi(NULL, instance, get_interface); } + diff --git a/native_client_sdk/src/libraries/nacl_io/ostypes.h b/native_client_sdk/src/libraries/nacl_io/ostypes.h index 5adc6fe5bd..084ec3319f 100644 --- a/native_client_sdk/src/libraries/nacl_io/ostypes.h +++ b/native_client_sdk/src/libraries/nacl_io/ostypes.h @@ -13,6 +13,8 @@ typedef int mode_t; typedef SSIZE_T ssize_t; +typedef int uid_t; +typedef int gid_t; #endif diff --git a/native_client_sdk/src/libraries/nacl_io/osunistd.h b/native_client_sdk/src/libraries/nacl_io/osunistd.h new file mode 100644 index 0000000000..c5da0efe0a --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/osunistd.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2013 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. + */ +#ifndef LIBRARIES_NACL_IO_OSUNISTD_H_ +#define LIBRARIES_NACL_IO_OSUNISTD_H_ + +#if defined(WIN32) + +#define R_OK 4 +#define W_OK 2 +#define X_OK 1 +#define F_OK 0 + +#else + +#include <unistd.h> + +#endif + +#endif // LIBRARIES_NACL_IO_OSUNISTD_H_ + diff --git a/native_client_sdk/src/libraries/nacl_io/osutime.h b/native_client_sdk/src/libraries/nacl_io/osutime.h new file mode 100644 index 0000000000..03d98b99a2 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/osutime.h @@ -0,0 +1,15 @@ +/* Copyright 2013 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. + */ +#ifndef LIBRARIES_NACL_IO_OSUTIME_H_ +#define LIBRARIES_NACL_IO_OSUTIME_H_ + +#if defined(WIN32) +#define utimbuf _utimbuf +#endif + +struct utimbuf; + +#endif // LIBRARIES_NACL_IO_OSUTIME_H_ + diff --git a/native_client_sdk/src/libraries/nacl_io/path.cc b/native_client_sdk/src/libraries/nacl_io/path.cc index 6cab36a441..88b6413589 100644 --- a/native_client_sdk/src/libraries/nacl_io/path.cc +++ b/native_client_sdk/src/libraries/nacl_io/path.cc @@ -201,3 +201,4 @@ Path& Path::operator =(const Path& p) { Path& Path::operator =(const std::string& p) { return Set(p); } + diff --git a/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.cc b/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.cc index ea74756f0d..861649d1c3 100644 --- a/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.cc +++ b/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.cc @@ -136,3 +136,4 @@ int32_t RealPepperInterface::InitializeMessageLoop() { return PP_OK; } + diff --git a/native_client_sdk/src/libraries/nacl_io/typed_mount_factory.h b/native_client_sdk/src/libraries/nacl_io/typed_mount_factory.h new file mode 100644 index 0000000000..c859b0bb46 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/typed_mount_factory.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2013 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. + */ +#ifndef LIBRARIES_NACL_IO_TYPED_MOUNT_FACTORY_H_ +#define LIBRARIES_NACL_IO_TYPED_MOUNT_FACTORY_H_ + +#include "nacl_io/mount.h" +#include "nacl_io/mount_factory.h" + +template <typename T> +class TypedMountFactory : public MountFactory { + public: + virtual Error CreateMount(int dev, + StringMap_t& args, + PepperInterface* ppapi, + ScopedRef<Mount>* out_mount) { + ScopedRef<T> mnt(new T()); + Error error = mnt->Init(dev, args, ppapi); + if (error) + return error; + + *out_mount = mnt; + return 0; + } +}; + +#endif // LIBRARIES_NACL_IO_TYPED_MOUNT_FACTORY_H_ + diff --git a/native_client_sdk/src/libraries/nacl_io_test/example.dsc b/native_client_sdk/src/libraries/nacl_io_test/example.dsc index df934a56a2..b26cd7cd29 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/example.dsc +++ b/native_client_sdk/src/libraries/nacl_io_test/example.dsc @@ -13,20 +13,24 @@ 'kernel_proxy_mock.h', 'kernel_proxy_test.cc', 'kernel_wrap_test.cc', + 'main.cc', 'mock_util.h', - 'module.cc', - 'mount_node_test.cc', 'mount_html5fs_test.cc', 'mount_http_test.cc', + 'mount_mock.cc', + 'mount_mock.h', + 'mount_node_mock.cc', + 'mount_node_mock.h', + 'mount_node_test.cc', 'mount_test.cc', 'path_test.cc', 'pepper_interface_mock.cc', 'pepper_interface_mock.h', ], - 'DEPS': ['nacl_io'], + 'DEPS': ['ppapi_simple', 'nacl_io'], # Order matters here: gtest has a "main" function that will be used if # referenced before ppapi. - 'LIBS': ['gtest_ppapi', 'gmock', 'ppapi_cpp', 'ppapi', 'gtest', 'pthread'], + 'LIBS': ['gmock', 'ppapi_cpp', 'ppapi', 'gtest', 'pthread'], 'INCLUDES': ['$(NACL_SDK_ROOT)/include/gtest/internal'], } ], diff --git a/native_client_sdk/src/libraries/nacl_io_test/example.js b/native_client_sdk/src/libraries/nacl_io_test/example.js index 2d93739d0f..bd175944b9 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/example.js +++ b/native_client_sdk/src/libraries/nacl_io_test/example.js @@ -6,19 +6,80 @@ function moduleDidLoad() { // The module is not hidden by default so we can easily see if the plugin // failed to load. common.hideModule(); - common.naclModule.postMessage('RunGTest'); +} + +var currentTestEl = null; + +function startCommand(testName) { + var testListEl = document.getElementById('tests'); + var testEl = document.createElement('li'); + var testRowEl = document.createElement('div'); + var testNameEl = document.createElement('span'); + var testResultEl = document.createElement('span'); + testRowEl.classList.add('row'); + testNameEl.classList.add('name'); + testNameEl.textContent = testName; + testResultEl.classList.add('result'); + testRowEl.appendChild(testNameEl); + testRowEl.appendChild(testResultEl); + testEl.appendChild(testRowEl); + testListEl.appendChild(testEl); + + currentTestEl = testEl; +} + +function failCommand(fileName, lineNumber, summary) { + var testMessageEl = document.createElement('pre'); + testMessageEl.textContent += fileName + ':' + lineNumber + ': ' + summary; + currentTestEl.appendChild(testMessageEl); +} + +function endCommand(testName, testResult) { + var testRowEl = currentTestEl.querySelector('.row'); + var testResultEl = currentTestEl.querySelector('.result'); + testRowEl.classList.add(testResult); + testResultEl.textContent = testResult; } function handleMessage(event) { - var logEl = document.getElementById('log'); var msg = event.data; + var firstColon = msg.indexOf(':'); + var cmd = msg.substr(0, firstColon); + var cmdFunctionName = cmd + 'Command'; + var cmdFunction = window[cmdFunctionName]; + + if (typeof(cmdFunction) !== 'function') { + console.log('Unknown command: ' + cmd); + console.log(' message: ' + msg); + return; + } + + var argCount = cmdFunction.length; + + // Don't use split, because it will split all commas (for example any commas + // in the test failure summary). + var argList = msg.substr(firstColon + 1); + args = []; + for (var i = 0; i < argCount - 1; ++i) { + var arg; + var comma = argList.indexOf(','); + if (comma === -1) { + if (i !== argCount - 1) { + console.log('Bad arg count to command "' + cmd + '", expected ' + + argCount); + console.log(' message: ' + msg); + } else { + arg = argList; + } + } else { + arg = argList.substr(0, comma); + argList = argList.substr(comma + 1); + } + args.push(arg); + } - // Perform some basic escaping. - msg = msg.replace(/&/g, '&') - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); + // Last argument is the rest of the message. + args.push(argList); - logEl.innerHTML += msg + '\n'; + cmdFunction.apply(null, args); } diff --git a/native_client_sdk/src/libraries/nacl_io_test/index.html b/native_client_sdk/src/libraries/nacl_io_test/index.html index 424f557e13..ba54317929 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/index.html +++ b/native_client_sdk/src/libraries/nacl_io_test/index.html @@ -11,6 +11,11 @@ found in the LICENSE file. <title>{{title}}</title> <script type="text/javascript" src="common.js"></script> <script type="text/javascript" src="example.js"></script> + <style> + .result { padding-left: 10px; } + .ok { background-color: #0f0; } + .failed { background-color: #f00; } + </style> </head> <body {{attrs}}> <h1>{{title}}</h1> @@ -18,6 +23,6 @@ found in the LICENSE file. <!-- The NaCl plugin will be embedded inside the element with id "listener". See common.js.--> <div id="listener"></div> - <pre id="log"></pre> + <ul id="tests" style="list-style:none;"></ul> </body> </html> diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc index 9e6f4832a7..7f7f7eadd6 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc @@ -20,178 +20,172 @@ namespace { -class MountRefMock : public Mount { +class MountNodeRefMock : public MountNode { public: - MountRefMock(int* mount_count, int* handle_count) - : mount_count(mount_count), handle_count(handle_count) { - (*mount_count)++; - } + MountNodeRefMock(Mount* mnt) : MountNode(mnt) {} +}; - ~MountRefMock() { (*mount_count)--; } +class MountRefMock : public Mount { + public: + MountRefMock() {} + ~MountRefMock() {} public: - Error Open(const Path& path, int mode, MountNode** out_node) { - *out_node = NULL; + Error Access(const Path& path, int a_mode) { return ENOSYS; } + Error Open(const Path& path, int mode, ScopedMountNode* out_node) { + out_node->reset(NULL); return ENOSYS; } Error Unlink(const Path& path) { return 0; } Error Mkdir(const Path& path, int permissions) { return 0; } Error Rmdir(const Path& path) { return 0; } Error Remove(const Path& path) { return 0; } - - public: - int* mount_count; - int* handle_count; }; class KernelHandleRefMock : public KernelHandle { public: - KernelHandleRefMock(Mount* mnt, MountNode* node) - : KernelHandle(mnt, node) { - MountRefMock* mock_mount = static_cast<MountRefMock*>(mnt); - (*mock_mount->handle_count)++; - } + KernelHandleRefMock(const ScopedMount& mnt, const ScopedMountNode& node) + : KernelHandle(mnt, node) {} - ~KernelHandleRefMock() { - MountRefMock* mock_mount = static_cast<MountRefMock*>(mount_); - (*mock_mount->handle_count)--; - } + ~KernelHandleRefMock() {} }; class KernelObjectTest : public ::testing::Test { public: - KernelObjectTest() : mount_count(0), handle_count(0) { + KernelObjectTest() { proxy = new KernelObject; - mnt = new MountRefMock(&mount_count, &handle_count); + mnt.reset(new MountRefMock()); + node.reset(new MountNodeRefMock(mnt.get())); } ~KernelObjectTest() { // mnt is ref-counted, it doesn't need to be explicitly deleted. + node.reset(NULL); + mnt.reset(NULL); delete proxy; } KernelObject* proxy; - MountRefMock* mnt; - int mount_count; - int handle_count; + ScopedMount mnt; + ScopedMountNode node; }; } // namespace -TEST_F(KernelObjectTest, Referencing) { - KernelHandle* handle = new KernelHandleRefMock(mnt, NULL); - KernelHandle* handle2 = new KernelHandleRefMock(mnt, NULL); - KernelHandle* result_handle = NULL; +#include <nacl_io/mount_mem.h> +#include <nacl_io/mount_http.h> - // Objects should have one ref when we start +TEST_F(KernelObjectTest, Referencing) { + // The mount and node should have 1 ref count at this point EXPECT_EQ(1, mnt->RefCount()); - EXPECT_EQ(1, handle->RefCount()); + EXPECT_EQ(1, node->RefCount()); - // Objects should have two refs when we get here - int fd1 = proxy->AllocateFD(handle); + // Pass the mount and node into a KernelHandle + KernelHandle* raw_handle = new KernelHandleRefMock(mnt, node); + ScopedKernelHandle handle_a(raw_handle); + + // The mount and node should have 1 ref count at this point + EXPECT_EQ(1, handle_a->RefCount()); + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); + + ScopedKernelHandle handle_b = handle_a; + + // There should be two references to the KernelHandle, the mount and node + // should be unchanged. + EXPECT_EQ(2, handle_a->RefCount()); + EXPECT_EQ(2, handle_b->RefCount()); + EXPECT_EQ(handle_a.get(), handle_b.get()); EXPECT_EQ(2, mnt->RefCount()); - EXPECT_EQ(2, handle->RefCount()); + EXPECT_EQ(2, node->RefCount()); - // If we "dup" the handle, we should bump the refs - int fd2 = proxy->AllocateFD(handle); - EXPECT_EQ(3, mnt->RefCount()); - EXPECT_EQ(3, handle->RefCount()); + // Allocating an FD should cause the KernelProxy to ref the handle and + // the node and mount should be unchanged. + int fd1 = proxy->AllocateFD(handle_a); + EXPECT_EQ(3, handle_a->RefCount()); + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); - // If use a new handle with the same values... bump the refs - int fd3 = proxy->AllocateFD(handle2); - EXPECT_EQ(4, mnt->RefCount()); - EXPECT_EQ(2, handle2->RefCount()); + // If we "dup" the handle, we should bump the ref count on the handle + int fd2 = proxy->AllocateFD(handle_b); + EXPECT_EQ(4, handle_a->RefCount()); + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); // Handles are expected to come out in order EXPECT_EQ(0, fd1); EXPECT_EQ(1, fd2); - EXPECT_EQ(2, fd3); + + // Now we "free" the handles, since the proxy should hold them. + handle_a.reset(NULL); + handle_b.reset(NULL); + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); // We should find the handle by either fd - EXPECT_EQ(0, proxy->AcquireHandle(fd1, &result_handle)); - EXPECT_EQ(handle, result_handle); - EXPECT_EQ(0, proxy->AcquireHandle(fd2, &result_handle)); - EXPECT_EQ(handle, result_handle); - - // A non existent fd should fail - EXPECT_EQ(EBADF, proxy->AcquireHandle(-1, &result_handle)); - EXPECT_EQ(NULL, result_handle); - EXPECT_EQ(EBADF, proxy->AcquireHandle(100, &result_handle)); - EXPECT_EQ(NULL, result_handle); - - // Acquiring the handle, should have ref'd it - EXPECT_EQ(4, mnt->RefCount()); - EXPECT_EQ(5, handle->RefCount()); - - // Release the handle for each call to acquire - proxy->ReleaseHandle(handle); - proxy->ReleaseHandle(handle); - - // Release the handle for each time we constructed something - proxy->ReleaseHandle(handle); - proxy->ReleaseHandle(handle2); - proxy->ReleaseMount(mnt); - - // We should now only have references used by the KernelProxy - EXPECT_EQ(2, handle->RefCount()); - EXPECT_EQ(1, handle2->RefCount()); - EXPECT_EQ(3, mnt->RefCount()); - - EXPECT_EQ(2, handle_count); - EXPECT_EQ(1, mount_count); + EXPECT_EQ(0, proxy->AcquireHandle(fd1, &handle_a)); + EXPECT_EQ(0, proxy->AcquireHandle(fd2, &handle_b)); + EXPECT_EQ(raw_handle, handle_a.get()); + EXPECT_EQ(raw_handle, handle_b.get()); - proxy->FreeFD(fd1); - EXPECT_EQ(2, handle_count); - EXPECT_EQ(1, mount_count); + EXPECT_EQ(4, handle_a->RefCount()); + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); + + // A non existent fd should fail, and handleA should decrement as handleB + // is released by the call. + EXPECT_EQ(EBADF, proxy->AcquireHandle(-1, &handle_b)); + EXPECT_EQ(NULL, handle_b.get()); + EXPECT_EQ(3, handle_a->RefCount()); + + EXPECT_EQ(EBADF, proxy->AcquireHandle(100, &handle_b)); + EXPECT_EQ(NULL, handle_b.get()); - proxy->FreeFD(fd3); - EXPECT_EQ(1, handle_count); - EXPECT_EQ(1, mount_count); + // Now only the KernelProxy should reference the KernelHandle in the + // FD to KernelHandle Map. + handle_a.reset(); + handle_b.reset(); + EXPECT_EQ(2, raw_handle->RefCount()); + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); proxy->FreeFD(fd2); - EXPECT_EQ(0, handle_count); - EXPECT_EQ(0, mount_count); + EXPECT_EQ(1, raw_handle->RefCount()); + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); + + proxy->FreeFD(fd1); + EXPECT_EQ(1, mnt->RefCount()); + EXPECT_EQ(1, node->RefCount()); } TEST_F(KernelObjectTest, FreeAndReassignFD) { - KernelHandle* result_handle = NULL; - - EXPECT_EQ(0, handle_count); - - KernelHandle* handle = new KernelHandleRefMock(mnt, NULL); - - EXPECT_EQ(1, handle_count); - EXPECT_EQ(1, handle->RefCount()); - - // Assign to a non-existent FD - proxy->FreeAndReassignFD(2, handle); - EXPECT_EQ(EBADF, proxy->AcquireHandle(0, &result_handle)); - EXPECT_EQ(NULL, result_handle); - EXPECT_EQ(EBADF, proxy->AcquireHandle(1, &result_handle)); - EXPECT_EQ(NULL, result_handle); - EXPECT_EQ(0, proxy->AcquireHandle(2, &result_handle)); - EXPECT_EQ(handle, result_handle); - proxy->ReleaseHandle(handle); - - EXPECT_EQ(1, handle_count); - EXPECT_EQ(2, handle->RefCount()); - - proxy->FreeAndReassignFD(0, handle); - EXPECT_EQ(0, proxy->AcquireHandle(0, &result_handle)); - EXPECT_EQ(handle, result_handle); - EXPECT_EQ(EBADF, proxy->AcquireHandle(1, &result_handle)); - EXPECT_EQ(NULL, result_handle); - EXPECT_EQ(0, proxy->AcquireHandle(2, &result_handle)); - EXPECT_EQ(handle, result_handle); - proxy->ReleaseHandle(handle); - proxy->ReleaseHandle(handle); - - EXPECT_EQ(1, handle_count); - EXPECT_EQ(3, handle->RefCount()); - - proxy->FreeFD(0); - proxy->FreeFD(2); - proxy->ReleaseHandle(handle); // handle is constructed with a refcount of 1. - - EXPECT_EQ(0, handle_count); + // The mount and node should have 1 ref count at this point + EXPECT_EQ(1, mnt->RefCount()); + EXPECT_EQ(1, node->RefCount()); + + KernelHandle* raw_handle = new KernelHandleRefMock(mnt, node); + ScopedKernelHandle handle(raw_handle); + + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); + EXPECT_EQ(1, raw_handle->RefCount()); + + int fd1 = proxy->AllocateFD(handle); + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); + EXPECT_EQ(2, raw_handle->RefCount()); + + proxy->FreeAndReassignFD(5, handle); + EXPECT_EQ(2, mnt->RefCount()); + EXPECT_EQ(2, node->RefCount()); + EXPECT_EQ(3, raw_handle->RefCount()); + + handle.reset(); + EXPECT_EQ(2, raw_handle->RefCount()); + + proxy->AcquireHandle(5, &handle); + EXPECT_EQ(3, raw_handle->RefCount()); + EXPECT_EQ(raw_handle, handle.get()); } + diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.cc index 5c4db1d905..367569ef13 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.cc @@ -6,11 +6,11 @@ #include "kernel_proxy_mock.h" #include "nacl_io/kernel_intercept.h" -KernelProxyMock::KernelProxyMock() { -} +KernelProxyMock::KernelProxyMock() {} KernelProxyMock::~KernelProxyMock() { // Uninitialize the kernel proxy so wrapped functions passthrough to their // unwrapped versions. ki_uninit(); } + diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h index d74e886bc8..defd7deb41 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h @@ -20,16 +20,20 @@ class KernelProxyMock : public KernelProxy { MOCK_METHOD2(access, int(const char*, int)); MOCK_METHOD1(chdir, int(const char*)); MOCK_METHOD2(chmod, int(const char*, mode_t)); + MOCK_METHOD3(chown, int(const char*, uid_t, gid_t)); MOCK_METHOD1(close, int(int)); MOCK_METHOD1(dup, int(int)); MOCK_METHOD2(dup2, int(int, int)); + MOCK_METHOD3(fchown, int(int, uid_t, gid_t)); MOCK_METHOD2(ftruncate, int(int, off_t)); MOCK_METHOD2(fstat, int(int, struct stat*)); MOCK_METHOD1(fsync, int(int)); MOCK_METHOD2(getcwd, char*(char*, size_t)); MOCK_METHOD3(getdents, int(int, void*, unsigned int)); MOCK_METHOD1(getwd, char*(char*)); + MOCK_METHOD3(ioctl, int(int, int, char*)); MOCK_METHOD1(isatty, int(int)); + MOCK_METHOD3(lchown, int(const char*, uid_t, gid_t)); MOCK_METHOD3(lseek, off_t(int, off_t, int)); MOCK_METHOD2(mkdir, int(const char*, mode_t)); MOCK_METHOD5(mount, int(const char*, const char*, const char*, unsigned long, @@ -41,6 +45,7 @@ class KernelProxyMock : public KernelProxy { MOCK_METHOD2(stat, int(const char*, struct stat*)); MOCK_METHOD1(umount, int(const char*)); MOCK_METHOD1(unlink, int(const char*)); + MOCK_METHOD2(utime, int(const char*, const struct utimbuf*)); MOCK_METHOD3(write, ssize_t(int, const void*, size_t)); MOCK_METHOD2(link, int(const char*, const char*)); MOCK_METHOD2(symlink, int(const char*, const char*)); diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc index 40f4ef7a15..70e76e17b9 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc @@ -11,19 +11,39 @@ #include <map> #include <string> -#include "nacl_io/kernel_handle.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "mount_mock.h" +#include "mount_node_mock.h" + #include "nacl_io/kernel_intercept.h" #include "nacl_io/kernel_proxy.h" #include "nacl_io/mount.h" #include "nacl_io/mount_mem.h" #include "nacl_io/osmman.h" #include "nacl_io/path.h" +#include "nacl_io/typed_mount_factory.h" -#include "gtest/gtest.h" +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::SetArgPointee; +using ::testing::StrEq; +using ::testing::WithArgs; + +namespace { + +class KernelProxyFriend : public KernelProxy { + public: + Mount* RootMount() { return mounts_["/"].get(); } +}; class KernelProxyTest : public ::testing::Test { public: - KernelProxyTest() : kp_(new KernelProxy) { + KernelProxyTest() : kp_(new KernelProxyFriend) { ki_init(kp_); // Unmount the passthrough FS and mount a memfs. EXPECT_EQ(0, kp_->umount("/")); @@ -35,10 +55,36 @@ class KernelProxyTest : public ::testing::Test { delete kp_; } - private: - KernelProxy* kp_; + protected: + KernelProxyFriend* kp_; }; +} // namespace + +TEST_F(KernelProxyTest, FileLeak) { + const size_t buffer_size = 1024; + char filename[128]; + int file_num; + int garbage[buffer_size]; + + MountMem* mount = (MountMem*)kp_->RootMount(); + ScopedMountNode root; + + EXPECT_EQ(0, mount->Open(Path("/"), O_RDONLY, &root)); + EXPECT_EQ(0, root->ChildCount()); + + for (file_num = 0; file_num < 4096; file_num++) { + sprintf(filename, "/foo%i.tmp", file_num++); + FILE* f = fopen(filename, "w"); + EXPECT_NE((FILE*)0, f); + EXPECT_EQ(1, root->ChildCount()); + EXPECT_EQ(buffer_size, fwrite(garbage, 1, buffer_size, f)); + fclose(f); + EXPECT_EQ(0, remove(filename)); + } + EXPECT_EQ(0, root->ChildCount()); +} + TEST_F(KernelProxyTest, WorkingDirectory) { char text[1024]; @@ -151,6 +197,7 @@ TEST_F(KernelProxyTest, MemMountLseek) { char buffer[4]; memset(&buffer[0], 0xfe, 4); EXPECT_EQ(9, ki_lseek(fd, -4, SEEK_END)); + EXPECT_EQ(9, ki_lseek(fd, 0, SEEK_CUR)); EXPECT_EQ(4, ki_read(fd, &buffer[0], 4)); EXPECT_EQ(0, memcmp("\0\0\0\0", buffer, 4)); } @@ -197,6 +244,8 @@ TEST_F(KernelProxyTest, MemMountDup) { // fd, new_fd, dup_fd -> "/bar" } +namespace { + StringMap_t g_StringMap; class MountMockInit : public MountMem { @@ -207,13 +256,14 @@ class MountMockInit : public MountMem { return EINVAL; return 0; } - ; + + friend class TypedMountFactory<MountMockInit>; }; class KernelProxyMountMock : public KernelProxy { virtual void Init(PepperInterface* ppapi) { KernelProxy::Init(NULL); - factories_["initfs"] = MountMockInit::Create<MountMockInit>; + factories_["initfs"] = new TypedMountFactory<MountMockInit>; } }; @@ -230,6 +280,8 @@ class KernelProxyMountTest : public ::testing::Test { KernelProxy* kp_; }; +} // namespace + TEST_F(KernelProxyMountTest, MountInit) { int res1 = ki_mount("/", "/mnt1", "initfs", 0, "false,foo=bar"); @@ -282,26 +334,28 @@ class MountNodeMockMMap : public MountNode { class MountMockMMap : public Mount { public: - virtual Error Open(const Path& path, int mode, MountNode** out_node) { - MountNodeMockMMap* node = new MountNodeMockMMap(this); - *out_node = node; + virtual Error Access(const Path& path, int a_mode) { return 0; } + virtual Error Open(const Path& path, int mode, ScopedMountNode* out_node) { + out_node->reset(new MountNodeMockMMap(this)); return 0; } - virtual Error OpenResource(const Path& path, MountNode** out_node) { - *out_node = NULL; + virtual Error OpenResource(const Path& path, ScopedMountNode* out_node) { + out_node->reset(NULL); return ENOSYS; } virtual Error Unlink(const Path& path) { return ENOSYS; } virtual Error Mkdir(const Path& path, int permissions) { return ENOSYS; } virtual Error Rmdir(const Path& path) { return ENOSYS; } virtual Error Remove(const Path& path) { return ENOSYS; } + + friend class TypedMountFactory<MountMockMMap>; }; class KernelProxyMockMMap : public KernelProxy { virtual void Init(PepperInterface* ppapi) { KernelProxy::Init(NULL); - factories_["mmapfs"] = MountMockInit::Create<MountMockMMap>; + factories_["mmapfs"] = new TypedMountFactory<MountMockMMap>; } }; @@ -345,3 +399,104 @@ TEST_F(KernelProxyMMapTest, MMap) { // We don't track regions, so the mmap count hasn't changed. EXPECT_EQ(3, g_MMapCount); } + +namespace { + +class SingletonMountFactory : public MountFactory { + public: + SingletonMountFactory(const ScopedMount& mount) : mount_(mount) {} + + virtual Error CreateMount(int dev, + StringMap_t& args, + PepperInterface* ppapi, + ScopedMount* out_mount) { + *out_mount = mount_; + return 0; + } + + private: + ScopedMount mount_; +}; + +class KernelProxyError : public KernelProxy { + public: + KernelProxyError() : mnt_(new MountMock) {} + + virtual void Init(PepperInterface* ppapi) { + KernelProxy::Init(ppapi); + factories_["testfs"] = new SingletonMountFactory(mnt_); + + EXPECT_CALL(*mnt_, Destroy()).Times(1); + } + + ScopedRef<MountMock> mnt() { return mnt_; } + + private: + ScopedRef<MountMock> mnt_; +}; + +class KernelProxyErrorTest : public ::testing::Test { + public: + KernelProxyErrorTest() : kp_(new KernelProxyError) { + ki_init(kp_); + // Unmount the passthrough FS and mount a testfs. + EXPECT_EQ(0, kp_->umount("/")); + EXPECT_EQ(0, kp_->mount("", "/", "testfs", 0, NULL)); + } + + ~KernelProxyErrorTest() { + ki_uninit(); + delete kp_; + } + + ScopedRef<MountMock> mnt() { return kp_->mnt(); } + + private: + KernelProxyError* kp_; +}; + +} // namespace + +TEST_F(KernelProxyErrorTest, WriteError) { + ScopedRef<MountMock> mock_mnt(mnt()); + ScopedRef<MountNodeMock> mock_node(new MountNodeMock(&*mock_mnt)); + EXPECT_CALL(*mock_mnt, Open(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mock_node), Return(0))); + + EXPECT_CALL(*mock_node, Write(_, _, _, _)) + .WillOnce(DoAll(SetArgPointee<3>(0), // Wrote 0 bytes. + Return(1234))); // Returned error 1234. + + EXPECT_CALL(*mock_node, Destroy()).Times(1); + + int fd = ki_open("/dummy", O_WRONLY); + EXPECT_NE(0, fd); + + char buf[20]; + EXPECT_EQ(-1, ki_write(fd, &buf[0], 20)); + // The Mount should be able to return whatever error it wants and have it + // propagate through. + EXPECT_EQ(1234, errno); +} + +TEST_F(KernelProxyErrorTest, ReadError) { + ScopedRef<MountMock> mock_mnt(mnt()); + ScopedRef<MountNodeMock> mock_node(new MountNodeMock(&*mock_mnt)); + EXPECT_CALL(*mock_mnt, Open(_, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mock_node), Return(0))); + + EXPECT_CALL(*mock_node, Read(_, _, _, _)) + .WillOnce(DoAll(SetArgPointee<3>(0), // Read 0 bytes. + Return(1234))); // Returned error 1234. + + EXPECT_CALL(*mock_node, Destroy()).Times(1); + + int fd = ki_open("/dummy", O_RDONLY); + EXPECT_NE(0, fd); + + char buf[20]; + EXPECT_EQ(-1, ki_read(fd, &buf[0], 20)); + // The Mount should be able to return whatever error it wants and have it + // propagate through. + EXPECT_EQ(1234, errno); +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc index 34d8472ff2..53856a46dd 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. +/* Copyright (c) 2013 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. */ @@ -73,6 +73,9 @@ void MakeDummyStatbuf(struct stat* statbuf) { statbuf->st_ctime = 11; } +const uid_t kDummyUid = 1001; +const gid_t kDummyGid = 1002; + class KernelWrapTest : public ::testing::Test { public: KernelWrapTest() { @@ -114,6 +117,13 @@ TEST_F(KernelWrapTest, chmod) { chmod("chmod", 23); } +TEST_F(KernelWrapTest, chown) { + uid_t uid = kDummyUid; + gid_t gid = kDummyGid; + EXPECT_CALL(mock, chown(StrEq("chown"), uid, gid)).Times(1); + chown("chown", uid, gid); +} + TEST_F(KernelWrapTest, close) { EXPECT_CALL(mock, close(34)).Times(1); close(34); @@ -129,6 +139,13 @@ TEST_F(KernelWrapTest, dup2) { dup2(123, 234); } +TEST_F(KernelWrapTest, fchown) { + uid_t uid = kDummyUid; + gid_t gid = kDummyGid; + EXPECT_CALL(mock, fchown(123, uid, gid)).Times(1); + fchown(123, uid, gid); +} + TEST_F(KernelWrapTest, fstat) { struct stat in_statbuf; MakeDummyStatbuf(&in_statbuf); @@ -174,11 +191,24 @@ TEST_F(KernelWrapTest, getwd) { #pragma GCC diagnostic warning "-Wdeprecated-declarations" #endif +TEST_F(KernelWrapTest, ioctl) { + char buffer[] = "ioctl"; + EXPECT_CALL(mock, ioctl(012, 345, StrEq("ioctl"))).Times(1); + ioctl(012, 345, buffer); +} + TEST_F(KernelWrapTest, isatty) { EXPECT_CALL(mock, isatty(678)).Times(1); isatty(678); } +TEST_F(KernelWrapTest, lchown) { + uid_t uid = kDummyUid; + gid_t gid = kDummyGid; + EXPECT_CALL(mock, lchown(StrEq("lchown"), uid, gid)).Times(1); + lchown("lchown", uid, gid); +} + TEST_F(KernelWrapTest, lseek) { EXPECT_CALL(mock, lseek(789, 891, 912)).Times(1); lseek(789, 891, 912); @@ -242,7 +272,14 @@ TEST_F(KernelWrapTest, unlink) { unlink("unlink"); } +TEST_F(KernelWrapTest, utime) { + const struct utimbuf* times; + EXPECT_CALL(mock, utime(StrEq("utime"), times)); + utime("utime", times); +} + TEST_F(KernelWrapTest, write) { EXPECT_CALL(mock, write(6789, NULL, 7891)).Times(1); write(6789, NULL, 7891); } + diff --git a/native_client_sdk/src/libraries/nacl_io_test/main.cc b/native_client_sdk/src/libraries/nacl_io_test/main.cc new file mode 100644 index 0000000000..f9adc0d594 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io_test/main.cc @@ -0,0 +1,55 @@ +// Copyright (c) 2013 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. + + +#include <string> + +#include "gtest/gtest.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/var.h" +#include "ppapi_simple/ps_main.h" + +#if defined(WIN32) +#include <Windows.h> +#undef PostMessage +#endif + +class GTestEventListener : public ::testing::EmptyTestEventListener { + public: + // TestEventListener overrides. + virtual void OnTestStart(const ::testing::TestInfo& test_info) { + std::stringstream msg; + msg << "start:" << test_info.test_case_name() << "." << test_info.name(); + pp::Instance(PSGetInstanceId()).PostMessage(msg.str()); + } + + virtual void OnTestPartResult( + const ::testing::TestPartResult& test_part_result) { + if (test_part_result.failed()) { + std::stringstream msg; + msg << "fail:" << test_part_result.file_name() << "," + << test_part_result.line_number() << "," + << test_part_result.summary(); + pp::Instance(PSGetInstanceId()).PostMessage(msg.str()); + } + } + + virtual void OnTestEnd(const ::testing::TestInfo& test_info) { + std::stringstream msg; + msg << "end:" << test_info.test_case_name() << "." << test_info.name() + << "," << (test_info.result()->Failed() ? "failed" : "ok"); + pp::Instance(PSGetInstanceId()).PostMessage(msg.str()); + } +}; + +int example_main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners() + .Append(new GTestEventListener()); + return RUN_ALL_TESTS(); +} + +// Register the function to call once the Instance Object is initialized. +// see: pappi_simple/ps_main.h +PPAPI_SIMPLE_REGISTER_MAIN(example_main); diff --git a/native_client_sdk/src/libraries/nacl_io_test/module.cc b/native_client_sdk/src/libraries/nacl_io_test/module.cc deleted file mode 100644 index 0eee65f108..0000000000 --- a/native_client_sdk/src/libraries/nacl_io_test/module.cc +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2012 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. -#include "gtest_ppapi/gtest_module.h" - -namespace pp { - -Module* CreateModule() { - return new GTestModule(); -} - -} // namespace pp diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc index e92a20aa5d..a14ef0fae0 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc @@ -18,6 +18,7 @@ #include "mock_util.h" #include "nacl_io/mount_html5fs.h" #include "nacl_io/osdirent.h" +#include "nacl_io/osunistd.h" #include "pepper_interface_mock.h" using ::testing::_; @@ -37,9 +38,7 @@ class MountHtml5FsMock : public MountHtml5Fs { Init(1, map, ppapi); } - ~MountHtml5FsMock() { - Destroy(); - } + ~MountHtml5FsMock() {} }; class MountHtml5FsTest : public ::testing::Test { @@ -99,8 +98,8 @@ class MountHtml5FsNodeTest : public MountHtml5FsTest { void InitNode(); protected: - MountHtml5FsMock* mnt_; - MountNode* node_; + ScopedRef<MountHtml5FsMock> mnt_; + ScopedMountNode node_; FileRefInterfaceMock* fileref_; FileIoInterfaceMock* fileio_; @@ -114,9 +113,7 @@ class MountHtml5FsNodeTest : public MountHtml5FsTest { const char MountHtml5FsNodeTest::path_[] = "/foo"; MountHtml5FsNodeTest::MountHtml5FsNodeTest() - : mnt_(NULL), - node_(NULL), - fileref_(NULL), + : fileref_(NULL), fileio_(NULL) { } @@ -126,10 +123,8 @@ void MountHtml5FsNodeTest::SetUp() { } void MountHtml5FsNodeTest::TearDown() { - if (mnt_) { - mnt_->ReleaseNode(node_); - delete mnt_; - } + node_.reset(); + mnt_.reset(); } void MountHtml5FsNodeTest::SetUpNodeExpectations(PP_FileType file_type) { @@ -162,12 +157,12 @@ void MountHtml5FsNodeTest::SetUpNodeExpectations(PP_FileType file_type) { void MountHtml5FsNodeTest::InitFilesystem() { StringMap_t map; - mnt_ = new MountHtml5FsMock(map, ppapi_); + mnt_.reset(new MountHtml5FsMock(map, ppapi_)); } void MountHtml5FsNodeTest::InitNode() { ASSERT_EQ(0, mnt_->Open(Path(path_), O_CREAT | O_RDWR, &node_)); - ASSERT_NE((MountNode*)NULL, node_); + ASSERT_NE((MountNode*)NULL, node_.get()); } // Node test where the filesystem is opened synchronously; that is, the @@ -316,7 +311,78 @@ TEST_F(MountHtml5FsTest, FilesystemType) { StringMap_t map; map["type"] = "PERSISTENT"; map["expected_size"] = "100"; - MountHtml5FsMock mnt(map, ppapi_); + ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); +} + +TEST_F(MountHtml5FsTest, Access) { + const char path[] = "/foo"; + const PP_Resource fileref_resource = 235; + const PP_Resource fileio_resource = 236; + + // These are the default values. + SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); + + FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); + FileIoInterfaceMock* fileio = ppapi_->GetFileIoInterface(); + + EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) + .WillOnce(Return(fileref_resource)); + PP_FileInfo info; + memset(&info, 0, sizeof(PP_FileInfo)); + info.type = PP_FILETYPE_REGULAR; + EXPECT_CALL(*fileref, Query(fileref_resource, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(info), + Return(int32_t(PP_OK)))); + EXPECT_CALL(*fileio, Create(instance_)).WillOnce(Return(fileio_resource)); + int32_t open_flags = PP_FILEOPENFLAG_READ; + EXPECT_CALL(*fileio, + Open(fileio_resource, fileref_resource, open_flags, _)) + .WillOnce(Return(int32_t(PP_OK))); + EXPECT_CALL(*fileio, Close(fileio_resource)); + EXPECT_CALL(*fileio, Flush(fileio_resource, _)); + EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource)); + EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); + + StringMap_t map; + ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); + + ASSERT_EQ(0, mnt->Access(Path(path), R_OK | W_OK | X_OK)); +} + +TEST_F(MountHtml5FsTest, AccessFileNotFound) { + const char path[] = "/foo"; + const PP_Resource fileref_resource = 235; + const PP_Resource fileio_resource = 236; + + // These are the default values. + SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); + + FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); + FileIoInterfaceMock* fileio = ppapi_->GetFileIoInterface(); + + // Report the file as missing. + EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) + .WillOnce(Return(fileref_resource)); + PP_FileInfo info; + memset(&info, 0, sizeof(PP_FileInfo)); + info.type = PP_FILETYPE_REGULAR; + EXPECT_CALL(*fileref, Query(fileref_resource, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(info), + Return(int32_t(PP_ERROR_FILENOTFOUND)))); + EXPECT_CALL(*fileio, Create(instance_)).WillOnce(Return(fileio_resource)); + int32_t open_flags = PP_FILEOPENFLAG_READ; + EXPECT_CALL(*fileio, + Open(fileio_resource, fileref_resource, open_flags, _)) + .WillOnce(Return(int32_t(PP_ERROR_FILENOTFOUND))); + EXPECT_CALL(*fileio, Close(fileio_resource)); + EXPECT_CALL(*fileio, Flush(fileio_resource, _)); + EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource)); + EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); + + StringMap_t map; + ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); + + ASSERT_EQ(ENOENT, mnt->Access(Path(path), F_OK)); } TEST_F(MountHtml5FsTest, Mkdir) { @@ -335,10 +401,10 @@ TEST_F(MountHtml5FsTest, Mkdir) { EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); StringMap_t map; - MountHtml5FsMock mnt(map, ppapi_); + ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); const int permissions = 0; // unused. - int32_t result = mnt.Mkdir(Path(path), permissions); + int32_t result = mnt->Mkdir(Path(path), permissions); ASSERT_EQ(0, result); } @@ -358,9 +424,9 @@ TEST_F(MountHtml5FsTest, Remove) { EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); StringMap_t map; - MountHtml5FsMock mnt(map, ppapi_); + ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); - int32_t result = mnt.Remove(Path(path)); + int32_t result = mnt->Remove(Path(path)); ASSERT_EQ(0, result); } @@ -558,3 +624,4 @@ TEST_F(MountHtml5FsNodeSyncDirTest, GetDents) { EXPECT_EQ(sizeof(dirent), dirents[1].d_reclen); EXPECT_STREQ(fileref_name_cstr_2, dirents[1].d_name); } + diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc index 1bc75068f1..2c77c508fa 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc @@ -16,6 +16,7 @@ #include "nacl_io/mount_http.h" #include "nacl_io/mount_node_dir.h" #include "nacl_io/osdirent.h" +#include "nacl_io/osunistd.h" #include "pepper_interface_mock.h" using ::testing::_; @@ -130,26 +131,27 @@ TEST_F(MountHttpTest, ParseManifest) { char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; EXPECT_EQ(0, mnt_->ParseManifest(manifest)); - MountNodeDir* root = NULL; + ScopedMountNode root; EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/"), &root)); - ASSERT_NE((MountNode*)NULL, root); + ASSERT_NE((MountNode*)NULL, root.get()); EXPECT_EQ(2, root->ChildCount()); - MountNodeDir* dir = NULL; + ScopedMountNode dir; EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/mydir"), &dir)); - ASSERT_NE((MountNode*)NULL, dir); + ASSERT_NE((MountNode*)NULL, dir.get()); EXPECT_EQ(1, dir->ChildCount()); - MountNode* node = mnt_->GetMap()["/mydir/foo"]; + MountNode* node = mnt_->GetMap()["/mydir/foo"].get(); EXPECT_NE((MountNode*)NULL, node); EXPECT_EQ(0, node->GetSize(&result_size)); EXPECT_EQ(123, result_size); // Since these files are cached thanks to the manifest, we can open them // without accessing the PPAPI URL API. - MountNode* foo = NULL; + ScopedMountNode foo; EXPECT_EQ(0, mnt_->Open(Path("/mydir/foo"), O_RDONLY, &foo)); - MountNode* bar = NULL; + + ScopedMountNode bar; EXPECT_EQ(0, mnt_->Open(Path("/thatdir/bar"), O_RDWR, &bar)); struct stat sfoo; @@ -176,12 +178,15 @@ class MountHttpNodeTest : public MountHttpTest { void ExpectHeaders(const char* headers); void OpenNode(); void SetResponse(int status_code, const char* headers); + // Set a response code, but expect the request to fail. Certain function calls + // expected by SetResponse are not expected here. + void SetResponseExpectFail(int status_code, const char* headers); void SetResponseBody(const char* body); void ResetMocks(); protected: MountHttpMock* mnt_; - MountNode* node_; + ScopedMountNode node_; VarInterfaceMock* var_; URLLoaderInterfaceMock* loader_; @@ -283,6 +288,18 @@ void MountHttpNodeTest::SetResponse(int status_code, const char* headers) { Return(headers))); } +void MountHttpNodeTest::SetResponseExpectFail(int status_code, + const char* headers) { + ON_CALL(*response_, GetProperty(response_resource_, _)) + .WillByDefault(Return(PP_MakeUndefined())); + + PP_Var var_headers = MakeString(348); + EXPECT_CALL(*response_, + GetProperty(response_resource_, + PP_URLRESPONSEPROPERTY_STATUSCODE)) + .WillOnce(Return(PP_MakeInt32(status_code))); +} + ACTION_P3(ReadResponseBodyAction, offset, body, body_length) { char* buf = static_cast<char*>(arg1); size_t read_length = arg2; @@ -310,7 +327,7 @@ void MountHttpNodeTest::SetResponseBody(const char* body) { void MountHttpNodeTest::OpenNode() { ASSERT_EQ(0, mnt_->Open(Path(path_), O_RDONLY, &node_)); - ASSERT_NE((MountNode*)NULL, node_); + ASSERT_NE((MountNode*)NULL, node_.get()); } void MountHttpNodeTest::ResetMocks() { @@ -322,8 +339,7 @@ void MountHttpNodeTest::ResetMocks() { } void MountHttpNodeTest::TearDown() { - if (node_) - mnt_->ReleaseNode(node_); + node_.reset(); delete mnt_; } @@ -337,6 +353,70 @@ TEST_F(MountHttpNodeTest, OpenAndCloseNoCache) { OpenNode(); } +TEST_F(MountHttpNodeTest, OpenAndCloseNotFound) { + StringMap_t smap; + smap["cache_content"] = "false"; + SetMountArgs(StringMap_t()); + ExpectOpen("HEAD"); + ExpectHeaders(""); + SetResponseExpectFail(404, ""); + ASSERT_EQ(ENOENT, mnt_->Open(Path(path_), O_RDONLY, &node_)); +} + +TEST_F(MountHttpNodeTest, OpenAndCloseServerError) { + StringMap_t smap; + smap["cache_content"] = "false"; + SetMountArgs(StringMap_t()); + ExpectOpen("HEAD"); + ExpectHeaders(""); + SetResponseExpectFail(500, ""); + ASSERT_EQ(EIO, mnt_->Open(Path(path_), O_RDONLY, &node_)); +} + +TEST_F(MountHttpNodeTest, GetStat) { + StringMap_t smap; + smap["cache_content"] = "false"; + SetMountArgs(StringMap_t()); + ExpectOpen("HEAD"); + ExpectHeaders(""); + SetResponse(200, "Content-Length: 42\n"); + OpenNode(); + + struct stat stat; + EXPECT_EQ(0, node_->GetStat(&stat)); + EXPECT_EQ(42, stat.st_size); +} + +TEST_F(MountHttpNodeTest, Access) { + StringMap_t smap; + smap["cache_content"] = "false"; + SetMountArgs(StringMap_t()); + ExpectOpen("HEAD"); + ExpectHeaders(""); + SetResponse(200, ""); + ASSERT_EQ(0, mnt_->Access(Path(path_), R_OK)); +} + +TEST_F(MountHttpNodeTest, AccessWrite) { + StringMap_t smap; + smap["cache_content"] = "false"; + SetMountArgs(StringMap_t()); + ExpectOpen("HEAD"); + ExpectHeaders(""); + SetResponse(200, ""); + ASSERT_EQ(EACCES, mnt_->Access(Path(path_), W_OK)); +} + +TEST_F(MountHttpNodeTest, AccessNotFound) { + StringMap_t smap; + smap["cache_content"] = "false"; + SetMountArgs(StringMap_t()); + ExpectOpen("HEAD"); + ExpectHeaders(""); + SetResponseExpectFail(404, ""); + ASSERT_EQ(ENOENT, mnt_->Access(Path(path_), R_OK)); +} + TEST_F(MountHttpNodeTest, ReadCached) { size_t result_size = 0; int result_bytes = 0; @@ -527,3 +607,4 @@ TEST_F(MountHttpNodeTest, ReadPartialNoServerSupport) { EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("abcdefghi", &buf[0]); } + diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_mock.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_mock.cc new file mode 100644 index 0000000000..4b69108e21 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_mock.cc @@ -0,0 +1,11 @@ +/* Copyright (c) 2013 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. + */ + +#include "mount_mock.h" + +MountMock::MountMock() {} + +MountMock::~MountMock() {} + diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_mock.h b/native_client_sdk/src/libraries/nacl_io_test/mount_mock.h new file mode 100644 index 0000000000..7ceebc47b9 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_mock.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2013 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. + */ + +#ifndef LIBRARIES_NACL_IO_TEST_MOUNT_MOCK_H_ +#define LIBRARIES_NACL_IO_TEST_MOUNT_MOCK_H_ + +#include "gmock/gmock.h" + +#include "nacl_io/mount.h" + +class MountMock : public Mount { + public: + MountMock(); + virtual ~MountMock(); + + MOCK_METHOD3(Init, Error(int, StringMap_t&, PepperInterface*)); + MOCK_METHOD0(Destroy, void()); + MOCK_METHOD2(Access, Error(const Path&, int)); + MOCK_METHOD3(Open, Error(const Path&, int, ScopedMountNode*)); + MOCK_METHOD2(OpenResource, Error(const Path&, ScopedMountNode*)); + MOCK_METHOD1(Unlink, Error(const Path&)); + MOCK_METHOD2(Mkdir, Error(const Path&, int)); + MOCK_METHOD1(Rmdir, Error(const Path&)); + MOCK_METHOD1(Remove, Error(const Path&)); +}; + +#endif // LIBRARIES_NACL_IO_TEST_MOUNT_MOCK_H_ + diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.cc new file mode 100644 index 0000000000..b925b56633 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.cc @@ -0,0 +1,11 @@ +/* Copyright (c) 2013 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. + */ + +#include "mount_node_mock.h" + +MountNodeMock::MountNodeMock(Mount* mount) : MountNode(mount) {} + +MountNodeMock::~MountNodeMock() {} + diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.h b/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.h new file mode 100644 index 0000000000..dbbfda1497 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2013 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. + */ + +#ifndef LIBRARIES_NACL_IO_TEST_MOUNT_NODE_MOCK_H_ +#define LIBRARIES_NACL_IO_TEST_MOUNT_NODE_MOCK_H_ + +#include "gmock/gmock.h" + +#include "nacl_io/mount.h" + +class MountNodeMock : public MountNode { + public: + explicit MountNodeMock(Mount*); + virtual ~MountNodeMock(); + + MOCK_METHOD1(Init, Error(int)); + MOCK_METHOD0(Destroy, void()); + MOCK_METHOD0(FSync, Error()); + MOCK_METHOD1(FTruncate, Error(off_t)); + MOCK_METHOD4(GetDents, Error(size_t, struct dirent*, size_t, int*)); + MOCK_METHOD1(GetStat, Error(struct stat*)); + MOCK_METHOD2(Ioctl, Error(int, char*)); + MOCK_METHOD4(Read, Error(size_t, void*, size_t, int*)); + MOCK_METHOD4(Write, Error(size_t, const void*, size_t, int*)); + MOCK_METHOD6(MMap, Error(void*, size_t, int, int, size_t, void**)); + MOCK_METHOD0(GetLinks, int()); + MOCK_METHOD0(GetMode, int()); + MOCK_METHOD0(GetType, int()); + MOCK_METHOD1(GetSize, Error(size_t*)); + MOCK_METHOD0(IsaDir, bool()); + MOCK_METHOD0(IsaFile, bool()); + MOCK_METHOD0(IsaTTY, bool()); + MOCK_METHOD0(ChildCount, int()); + MOCK_METHOD2(AddChild, Error(const std::string&, const ScopedMountNode&)); + MOCK_METHOD1(RemoveChild, Error(const std::string&)); + MOCK_METHOD2(FindChild, Error(const std::string&, ScopedMountNode*)); + MOCK_METHOD0(Link, void()); + MOCK_METHOD0(Unlink, void()); +}; + +#endif // LIBRARIES_NACL_IO_TEST_MOUNT_NODE_MOCK_H_ + diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc index bc6fa65ed6..794bbdfbf4 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc @@ -7,7 +7,9 @@ #include <fcntl.h> #include "nacl_io/error.h" +#include "nacl_io/ioctl.h" #include "nacl_io/kernel_proxy.h" +#include "nacl_io/mount_dev.h" #include "nacl_io/mount_node.h" #include "nacl_io/mount_node_dir.h" #include "nacl_io/mount_node_mem.h" @@ -26,13 +28,13 @@ class MockMemory : public MountNodeMem { ~MockMemory() { s_AllocNum--; } Error Init(int mode) { return MountNodeMem::Init(mode); } - Error AddChild(const std::string& name, MountNode* node) { + Error AddChild(const std::string& name, const ScopedMountNode& node) { return MountNodeMem::AddChild(name, node); } Error RemoveChild(const std::string& name) { return MountNodeMem::RemoveChild(name); } - Error FindChild(const std::string& name, MountNode** out_node) { + Error FindChild(const std::string& name, ScopedMountNode* out_node) { return MountNodeMem::FindChild(name, out_node); } void Link() { MountNodeMem::Link(); } @@ -49,13 +51,13 @@ class MockDir : public MountNodeDir { ~MockDir() { s_AllocNum--; } Error Init(int mode) { return MountNodeDir::Init(mode); } - Error AddChild(const std::string& name, MountNode* node) { + Error AddChild(const std::string& name, const ScopedMountNode& node) { return MountNodeDir::AddChild(name, node); } Error RemoveChild(const std::string& name) { return MountNodeDir::RemoveChild(name); } - Error FindChild(const std::string& name, MountNode** out_node) { + Error FindChild(const std::string& name, ScopedMountNode* out_node) { return MountNodeDir::FindChild(name, out_node); } void Link() { MountNodeDir::Link(); } @@ -67,7 +69,7 @@ class MockDir : public MountNodeDir { TEST(MountNodeTest, File) { MockMemory* file = new MockMemory; - MountNode* result_node = NULL; + ScopedMountNode result_node; size_t result_size = 0; int result_bytes = 0; @@ -80,7 +82,7 @@ TEST(MountNodeTest, File) { EXPECT_FALSE(file->IsaDir()); EXPECT_TRUE(file->IsaFile()); EXPECT_FALSE(file->IsaTTY()); - EXPECT_EQ(1, file->RefCount()); + EXPECT_EQ(0, file->RefCount()); // Test IO char buf1[1024]; @@ -111,17 +113,17 @@ TEST(MountNodeTest, File) { // Directory operations should fail struct dirent d; EXPECT_EQ(ENOTDIR, file->GetDents(0, &d, sizeof(d), &result_bytes)); - EXPECT_EQ(ENOTDIR, file->AddChild("", file)); + EXPECT_EQ(ENOTDIR, file->AddChild("", result_node)); EXPECT_EQ(ENOTDIR, file->RemoveChild("")); EXPECT_EQ(ENOTDIR, file->FindChild("", &result_node)); - EXPECT_EQ(NULL_NODE, result_node); + EXPECT_EQ(NULL_NODE, result_node.get()); delete file; } TEST(MountNodeTest, Directory) { MockDir* root = new MockDir(); - MountNode* result_node = NULL; + ScopedMountNode result_node; size_t result_size = 0; int result_bytes = 0; @@ -134,7 +136,7 @@ TEST(MountNodeTest, Directory) { EXPECT_TRUE(root->IsaDir()); EXPECT_FALSE(root->IsaFile()); EXPECT_FALSE(root->IsaTTY()); - EXPECT_EQ(1, root->RefCount()); + EXPECT_EQ(0, root->RefCount()); // IO operations should fail char buf1[1024]; @@ -144,10 +146,11 @@ TEST(MountNodeTest, Directory) { EXPECT_EQ(EISDIR, root->Write(0, buf1, sizeof(buf1), &result_bytes)); // Test directory operations - MockMemory* file = new MockMemory; - EXPECT_EQ(0, file->Init(S_IREAD | S_IWRITE)); + MockMemory* raw_file = new MockMemory; + EXPECT_EQ(0, raw_file->Init(S_IREAD | S_IWRITE)); + ScopedMountNode file(raw_file); - EXPECT_EQ(1, root->RefCount()); + EXPECT_EQ(0, root->RefCount()); EXPECT_EQ(1, file->RefCount()); EXPECT_EQ(0, root->AddChild("F1", file)); EXPECT_EQ(1, file->GetLinks()); @@ -169,14 +172,15 @@ TEST(MountNodeTest, Directory) { EXPECT_EQ(3, file->RefCount()); EXPECT_EQ(EEXIST, root->AddChild("F1", file)); EXPECT_EQ(2, file->GetLinks()); + EXPECT_EQ(3, file->RefCount()); EXPECT_EQ(2, s_AllocNum); EXPECT_EQ(0, root->FindChild("F1", &result_node)); - EXPECT_NE(NULL_NODE, result_node); + EXPECT_NE(NULL_NODE, result_node.get()); EXPECT_EQ(0, root->FindChild("F2", &result_node)); - EXPECT_NE(NULL_NODE, result_node); + EXPECT_NE(NULL_NODE, result_node.get()); EXPECT_EQ(ENOENT, root->FindChild("F3", &result_node)); - EXPECT_EQ(NULL_NODE, result_node); + EXPECT_EQ(NULL_NODE, result_node.get()); EXPECT_EQ(2, s_AllocNum); EXPECT_EQ(0, root->RemoveChild("F1")); @@ -187,9 +191,6 @@ TEST(MountNodeTest, Directory) { EXPECT_EQ(1, file->RefCount()); EXPECT_EQ(2, s_AllocNum); - file->Release(); + file.reset(); EXPECT_EQ(1, s_AllocNum); - root->Release(); - EXPECT_EQ(0, s_AllocNum); } - diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc index 245d13daf4..d9bb876070 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. +/* Copyright (c) 2013 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. */ @@ -6,15 +6,16 @@ #include <errno.h> #include <fcntl.h> #include <string.h> -#include <string> #include <sys/stat.h> +#include <string> +#include "gtest/gtest.h" +#include "nacl_io/ioctl.h" #include "nacl_io/mount.h" #include "nacl_io/mount_dev.h" #include "nacl_io/mount_mem.h" #include "nacl_io/osdirent.h" - -#include "gtest/gtest.h" +#include "nacl_io/osunistd.h" namespace { @@ -43,35 +44,48 @@ class MountDevMock : public MountDev { TEST(MountTest, Sanity) { MountMemMock* mnt = new MountMemMock(); - MountNode* file; - MountNode* root; - MountNode* result_node; + + ScopedMountNode file; + ScopedMountNode root; + ScopedMountNode result_node; + size_t result_size = 0; int result_bytes = 0; - char buf1[1024]; // A memory mount starts with one directory node: the root. EXPECT_EQ(1, mnt->num_nodes()); // Fail to open non existent file + EXPECT_EQ(ENOENT, mnt->Access(Path("/foo"), R_OK | W_OK)); EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &result_node)); - EXPECT_EQ(NULL, result_node); + EXPECT_EQ(NULL, result_node.get()); + EXPECT_EQ(1, mnt->num_nodes()); // Create a file EXPECT_EQ(0, mnt->Open(Path("/foo"), O_RDWR | O_CREAT, &file)); - EXPECT_NE(NULL_NODE, file); + EXPECT_NE(NULL_NODE, file.get()); if (file == NULL) return; - EXPECT_EQ(2, file->RefCount()); + + // We now have a directory and a file. The file has a two references + // one returned to the test, one for the name->inode map. EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(2, file->RefCount()); + EXPECT_EQ(0, mnt->Access(Path("/foo"), R_OK | W_OK)); + EXPECT_EQ(EACCES, mnt->Access(Path("/foo"), X_OK)); + // Write access should be allowed on the root directory. + EXPECT_EQ(0, mnt->Access(Path("/"), R_OK | W_OK)); + EXPECT_EQ(EACCES, mnt->Access(Path("/"), X_OK)); // Open the root directory for write should fail. EXPECT_EQ(EISDIR, mnt->Open(Path("/"), O_RDWR, &root)); + EXPECT_EQ(2, mnt->num_nodes()); - // Open the root directory + // Open the root directory, should not create a new file EXPECT_EQ(0, mnt->Open(Path("/"), O_RDONLY, &root)); - EXPECT_NE(NULL_NODE, root); + EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_NE(NULL_NODE, root.get()); if (NULL != root) { struct dirent dirs[2]; int len; @@ -82,11 +96,12 @@ TEST(MountTest, Sanity) { // Fail to re-create the same file EXPECT_EQ(EEXIST, mnt->Open(Path("/foo"), O_RDWR | O_CREAT | O_EXCL, &result_node)); - EXPECT_EQ(NULL_NODE, result_node); + EXPECT_EQ(NULL_NODE, result_node.get()); EXPECT_EQ(2, mnt->num_nodes()); // Fail to create a directory with the same name EXPECT_EQ(EEXIST, mnt->Mkdir(Path("/foo"), O_RDWR)); + EXPECT_EQ(2, mnt->num_nodes()); // Attempt to READ/WRITE EXPECT_EQ(0, file->GetSize(&result_size)); @@ -97,76 +112,108 @@ TEST(MountTest, Sanity) { EXPECT_EQ(sizeof(buf1), result_size); EXPECT_EQ(0, file->Read(0, buf1, sizeof(buf1), &result_bytes)); EXPECT_EQ(sizeof(buf1), result_bytes); + EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(2, file->RefCount()); - // Attempt to open the same file + // Attempt to open the same file, create another ref to it, but does not + // create a new file. EXPECT_EQ(0, mnt->Open(Path("/foo"), O_RDWR | O_CREAT, &result_node)); - EXPECT_EQ(file, result_node); - EXPECT_EQ(0, file->GetSize(&result_size)); - EXPECT_EQ(sizeof(buf1), result_size); EXPECT_EQ(3, file->RefCount()); EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(file.get(), result_node.get()); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(sizeof(buf1), result_size); - // Attempt to close and delete the file - mnt->ReleaseNode(file); + // Remove our references so that only the Mount holds it + file.reset(); + result_node.reset(); EXPECT_EQ(2, mnt->num_nodes()); + + // This should have deleted the object EXPECT_EQ(0, mnt->Unlink(Path("/foo"))); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(1, mnt->num_nodes()); + + // We should fail to find it EXPECT_EQ(ENOENT, mnt->Unlink(Path("/foo"))); - EXPECT_EQ(2, mnt->num_nodes()); - mnt->ReleaseNode(file); EXPECT_EQ(1, mnt->num_nodes()); // Recreate foo as a directory EXPECT_EQ(0, mnt->Mkdir(Path("/foo"), O_RDWR)); + EXPECT_EQ(2, mnt->num_nodes()); // Create a file (exclusively) EXPECT_EQ(0, mnt->Open(Path("/foo/bar"), O_RDWR | O_CREAT | O_EXCL, &file)); - EXPECT_NE(NULL_NODE, file); + EXPECT_NE(NULL_NODE, file.get()); if (NULL == file) return; + EXPECT_EQ(2, file->RefCount()); + EXPECT_EQ(3, mnt->num_nodes()); - // Attempt to delete the directory + // Attempt to delete the directory and fail EXPECT_EQ(ENOTEMPTY, mnt->Rmdir(Path("/foo"))); + EXPECT_EQ(2, root->RefCount()); + EXPECT_EQ(2, file->RefCount()); + EXPECT_EQ(3, mnt->num_nodes()); - // Unlink the file, then delete the directory + // Unlink the file, we should have the only file ref at this point. EXPECT_EQ(0, mnt->Unlink(Path("/foo/bar"))); - EXPECT_EQ(0, mnt->Rmdir(Path("/foo"))); + EXPECT_EQ(2, root->RefCount()); + EXPECT_EQ(1, file->RefCount()); + EXPECT_EQ(3, mnt->num_nodes()); + + + // Deref the file, to make it go away + file.reset(); EXPECT_EQ(2, mnt->num_nodes()); - mnt->ReleaseNode(file); + + // Deref the directory + EXPECT_EQ(0, mnt->Rmdir(Path("/foo"))); EXPECT_EQ(1, mnt->num_nodes()); // Verify the directory is gone + EXPECT_EQ(ENOENT, mnt->Access(Path("/foo"), F_OK)); EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &file)); - EXPECT_EQ(NULL_NODE, file); + EXPECT_EQ(NULL_NODE, file.get()); } TEST(MountTest, MemMountRemove) { MountMemMock* mnt = new MountMemMock(); - MountNode* file; - MountNode* result_node; + ScopedMountNode file; + ScopedMountNode result_node; EXPECT_EQ(0, mnt->Mkdir(Path("/dir"), O_RDWR)); EXPECT_EQ(0, mnt->Open(Path("/file"), O_RDWR | O_CREAT | O_EXCL, &file)); - EXPECT_NE(NULL_NODE, file); - mnt->ReleaseNode(file); + EXPECT_NE(NULL_NODE, file.get()); + EXPECT_EQ(3, mnt->num_nodes()); + file.reset(); EXPECT_EQ(0, mnt->Remove(Path("/dir"))); + EXPECT_EQ(2, mnt->num_nodes()); EXPECT_EQ(0, mnt->Remove(Path("/file"))); + EXPECT_EQ(1, mnt->num_nodes()); EXPECT_EQ(ENOENT, mnt->Open(Path("/dir/foo"), O_CREAT | O_RDWR, &result_node)); - EXPECT_EQ(NULL_NODE, result_node); + EXPECT_EQ(NULL_NODE, result_node.get()); EXPECT_EQ(ENOENT, mnt->Open(Path("/file"), O_RDONLY, &result_node)); - EXPECT_EQ(NULL_NODE, result_node); + EXPECT_EQ(NULL_NODE, result_node.get()); +} + +TEST(MountTest, DevAccess) { + // Should not be able to open non-existent file. + MountDevMock* mnt = new MountDevMock(); + ASSERT_EQ(ENOENT, mnt->Access(Path("/foo"), F_OK)); } TEST(MountTest, DevNull) { MountDevMock* mnt = new MountDevMock(); - MountNode* dev_null = NULL; + ScopedMountNode dev_null; int result_bytes = 0; + ASSERT_EQ(0, mnt->Access(Path("/null"), R_OK | W_OK)); + ASSERT_EQ(EACCES, mnt->Access(Path("/null"), X_OK)); ASSERT_EQ(0, mnt->Open(Path("/null"), O_RDWR, &dev_null)); - ASSERT_NE(NULL_NODE, dev_null); + ASSERT_NE(NULL_NODE, dev_null.get()); // Writing to /dev/null should write everything. const char msg[] = "Dummy test message."; @@ -178,16 +225,17 @@ TEST(MountTest, DevNull) { char buffer[kBufferLength]; EXPECT_EQ(0, dev_null->Read(0, &buffer[0], kBufferLength, &result_bytes)); EXPECT_EQ(0, result_bytes); - mnt->ReleaseNode(dev_null); } TEST(MountTest, DevZero) { MountDevMock* mnt = new MountDevMock(); - MountNode* dev_zero = NULL; + ScopedMountNode dev_zero; int result_bytes = 0; + ASSERT_EQ(0, mnt->Access(Path("/zero"), R_OK | W_OK)); + ASSERT_EQ(EACCES, mnt->Access(Path("/zero"), X_OK)); ASSERT_EQ(0, mnt->Open(Path("/zero"), O_RDWR, &dev_zero)); - ASSERT_NE(NULL_NODE, dev_zero); + ASSERT_NE(NULL_NODE, dev_zero.get()); // Writing to /dev/zero should write everything. const char msg[] = "Dummy test message."; @@ -205,16 +253,17 @@ TEST(MountTest, DevZero) { char zero_buffer[kBufferLength]; memset(&zero_buffer[0], 0, kBufferLength); EXPECT_EQ(0, memcmp(&buffer[0], &zero_buffer[0], kBufferLength)); - mnt->ReleaseNode(dev_zero); } TEST(MountTest, DevUrandom) { MountDevMock* mnt = new MountDevMock(); - MountNode* dev_urandom = NULL; + ScopedMountNode dev_urandom; int result_bytes = 0; + ASSERT_EQ(0, mnt->Access(Path("/urandom"), R_OK | W_OK)); + ASSERT_EQ(EACCES, mnt->Access(Path("/urandom"), X_OK)); ASSERT_EQ(0, mnt->Open(Path("/urandom"), O_RDWR, &dev_urandom)); - ASSERT_NE(NULL_NODE, dev_urandom); + ASSERT_NE(NULL_NODE, dev_urandom.get()); // Writing to /dev/urandom should write everything. const char msg[] = "Dummy test message."; @@ -250,3 +299,68 @@ TEST(MountTest, DevUrandom) { // Approximate chi-squared value for p-value 0.05, 255 degrees-of-freedom. EXPECT_LE(chi_squared, 293.24); } + + +TEST(MountTest, DevTty) { + MountDevMock* mnt = new MountDevMock(); + ScopedMountNode dev_tty; + + ASSERT_EQ(0, mnt->Access(Path("/tty"), R_OK | W_OK)); + ASSERT_EQ(EACCES, mnt->Access(Path("/tty"), X_OK)); + ASSERT_EQ(0, mnt->Open(Path("/tty"), O_RDWR, &dev_tty)); + ASSERT_NE(NULL_NODE, dev_tty.get()); + + // 123 is not a valid ioctl request. + EXPECT_EQ(EINVAL, dev_tty->Ioctl(123, NULL)); + + // TIOCNACLPREFIX is, it should set the prefix. + std::string prefix("__my_awesome_prefix__"); + EXPECT_EQ(0, dev_tty->Ioctl(TIOCNACLPREFIX, + const_cast<char*>(prefix.c_str()))); + + // Now let's try sending some data over. + // First we create the message. + std::string content("hello, how are you?"); + std::string message = prefix.append(content); + struct tioc_nacl_input_string packaged_message; + packaged_message.length = message.size(); + packaged_message.buffer = message.data(); + + // Now we make buffer we'll read into. + // We fill the buffer and a backup buffer with arbitrary data + // and compare them after reading to make sure read doesn't + // clobber parts of the buffer it shouldn't. + int bytes_read; + char buffer[100]; + char backup_buffer[100]; + memset(buffer, 'a', 100); + memset(backup_buffer, 'a', 100); + + // Now we actually send the data + EXPECT_EQ(0, dev_tty->Ioctl(TIOCNACLINPUT, + reinterpret_cast<char*>(&packaged_message))); + + // We read a small chunk first to ensure it doesn't give us + // more than we ask for. + EXPECT_EQ(0, dev_tty->Read(0, buffer, 5, &bytes_read)); + EXPECT_EQ(bytes_read, 5); + EXPECT_EQ(0, memcmp(content.data(), buffer, 5)); + EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, 95)); + + // Now we ask for more data than is left in the tty, to ensure + // it doesn't give us more than is there. + EXPECT_EQ(0, dev_tty->Read(0, buffer + 5, 95, &bytes_read)); + EXPECT_EQ(bytes_read, content.size() - 5); + EXPECT_EQ(0, memcmp(content.data(), buffer, content.size())); + EXPECT_EQ(0, memcmp(buffer + content.size(), + backup_buffer + content.size(), + 100 - content.size())); + + // Now we try to send something with an invalid prefix + std::string bogus_message("Woah there, this message has no valid prefix"); + struct tioc_nacl_input_string bogus_pack; + bogus_pack.length = bogus_message.size(); + bogus_pack.buffer = bogus_message.data(); + EXPECT_EQ(ENOTTY, dev_tty->Ioctl(TIOCNACLINPUT, + reinterpret_cast<char*>(&bogus_pack))); +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/path_test.cc b/native_client_sdk/src/libraries/nacl_io_test/path_test.cc index 3f8c91803e..9a8d980b34 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/path_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/path_test.cc @@ -256,3 +256,4 @@ TEST(PathTest, Range) { EXPECT_EQ("an/absolute", p.Range(1, 3)); EXPECT_EQ("absolute", p.Range(2, 3)); } + diff --git a/native_client_sdk/src/libraries/nacl_io_test/pepper_interface_mock.cc b/native_client_sdk/src/libraries/nacl_io_test/pepper_interface_mock.cc index 95699256ae..a3418a6d34 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/pepper_interface_mock.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/pepper_interface_mock.cc @@ -49,3 +49,4 @@ PP_Instance PepperInterfaceMock::GetInstance() { BaseClass##Mock::~BaseClass##Mock() { \ } #include "nacl_io/pepper/all_interfaces.h" + diff --git a/native_client_sdk/src/libraries/ppapi/library.dsc b/native_client_sdk/src/libraries/ppapi/library.dsc index 6b35983beb..7e2f240595 100644 --- a/native_client_sdk/src/libraries/ppapi/library.dsc +++ b/native_client_sdk/src/libraries/ppapi/library.dsc @@ -1,5 +1,11 @@ { - 'TOOLS': ['win', 'linux'], + 'TOOLS': ['win', 'linux', 'mac'], + 'SEARCH': [ + '.', + '../../../../ppapi/c', + '../../../../ppapi/c/dev', + '../../../../ppapi/c/extensions/dev', + ], 'TARGETS': [ { 'NAME' : 'ppapi', @@ -9,6 +15,130 @@ ], } ], + 'HEADERS': [ + { + 'FILES': [ + 'pp_array_output.h', + 'ppb_audio_config.h', + 'ppb_audio.h', + 'ppb_console.h', + 'ppb_core.h', + 'ppb_file_io.h', + 'ppb_file_ref.h', + 'ppb_file_system.h', + 'ppb_fullscreen.h', + 'ppb_gamepad.h', + 'ppb_graphics_2d.h', + 'ppb_graphics_3d.h', + 'ppb.h', + 'ppb_host_resolver.h', + 'ppb_image_data.h', + 'ppb_input_event.h', + 'ppb_instance.h', + 'ppb_message_loop.h', + 'ppb_messaging.h', + 'ppb_mouse_cursor.h', + 'ppb_mouse_lock.h', + 'ppb_net_address.h', + 'ppb_network_proxy.h', + 'pp_bool.h', + 'ppb_opengles2.h', + 'ppb_tcp_socket.h', + 'ppb_udp_socket.h', + 'ppb_url_loader.h', + 'ppb_url_request_info.h', + 'ppb_url_response_info.h', + 'ppb_var_array_buffer.h', + 'ppb_var_array.h', + 'ppb_var_dictionary.h', + 'ppb_var.h', + 'ppb_view.h', + 'ppb_websocket.h', + 'pp_completion_callback.h', + 'pp_directory_entry.h', + 'pp_errors.h', + 'pp_file_info.h', + 'pp_graphics_3d.h', + 'pp_input_event.h', + 'pp_instance.h', + 'pp_macros.h', + 'pp_module.h', + 'ppp_graphics_3d.h', + 'ppp.h', + 'ppp_input_event.h', + 'ppp_instance.h', + 'ppp_messaging.h', + 'ppp_mouse_lock.h', + 'pp_point.h', + 'pp_rect.h', + 'pp_resource.h', + 'pp_size.h', + 'pp_stdint.h', + 'pp_time.h', + 'pp_touch_point.h', + 'pp_var.h', + ], + 'DEST': 'include/ppapi/c', + }, + { + 'FILES': [ + 'deprecated_bool.h', + 'ppb_audio_input_dev.h', + 'ppb_buffer_dev.h', + 'ppb_char_set_dev.h', + 'ppb_crypto_dev.h', + 'ppb_cursor_control_dev.h', + 'ppb_device_ref_dev.h', + 'ppb_file_chooser_dev.h', + 'ppb_find_dev.h', + 'ppb_font_dev.h', + 'ppb_gles_chromium_texture_mapping_dev.h', + 'ppb_graphics_2d_dev.h', + 'ppb_ime_input_event_dev.h', + 'ppb_keyboard_input_event_dev.h', + 'ppb_memory_dev.h', + 'ppb_opengles2ext_dev.h', + 'ppb_printing_dev.h', + 'ppb_resource_array_dev.h', + 'ppb_scrollbar_dev.h', + 'ppb_testing_dev.h', + 'ppb_text_input_dev.h', + 'ppb_trace_event_dev.h', + 'ppb_truetype_font_dev.h', + 'ppb_url_util_dev.h', + 'ppb_var_deprecated.h', + 'ppb_video_capture_dev.h', + 'ppb_video_decoder_dev.h', + 'ppb_view_dev.h', + 'ppb_widget_dev.h', + 'ppb_zoom_dev.h', + 'pp_cursor_type_dev.h', + 'ppp_class_deprecated.h', + 'ppp_find_dev.h', + 'ppp_network_state_dev.h', + 'ppp_printing_dev.h', + 'pp_print_settings_dev.h', + 'ppp_scrollbar_dev.h', + 'ppp_selection_dev.h', + 'ppp_text_input_dev.h', + 'ppp_video_capture_dev.h', + 'ppp_video_decoder_dev.h', + 'ppp_widget_dev.h', + 'ppp_zoom_dev.h', + 'pp_video_capture_dev.h', + 'pp_video_dev.h', + ], + 'DEST': 'include/ppapi/c/dev', + }, + { + 'FILES': [ + 'ppb_ext_alarms_dev.h', + 'ppb_ext_events_dev.h', + 'ppb_ext_socket_dev.h', + ], + 'DEST': 'include/ppapi/c/extensions/dev', + }, + ], 'DEST': 'src', 'NAME': 'ppapi', } diff --git a/native_client_sdk/src/libraries/ppapi_cpp/library.dsc b/native_client_sdk/src/libraries/ppapi_cpp/library.dsc index e6ffb6c26c..061685c46d 100644 --- a/native_client_sdk/src/libraries/ppapi_cpp/library.dsc +++ b/native_client_sdk/src/libraries/ppapi_cpp/library.dsc @@ -3,6 +3,9 @@ 'SEARCH': [ '.', '../../../../ppapi/cpp', + '../../../../ppapi/cpp/dev', + '../../../../ppapi/cpp/extensions', + '../../../../ppapi/cpp/extensions/dev', '../../../../ppapi/utility', '../../../../ppapi/utility/graphics', '../../../../ppapi/utility/threading', @@ -13,46 +16,232 @@ 'NAME' : 'ppapi_cpp', 'TYPE' : 'lib', 'SOURCES' : [ - 'ppp_entrypoints.cc', - 'array_output.cc', - 'audio.cc', - 'audio_config.cc', - 'core.cc', - 'directory_entry.cc', - 'file_io.cc', - 'file_ref.cc', - 'file_system.cc', - 'fullscreen.cc', - 'graphics_2d.cc', - 'graphics_3d.cc', - 'graphics_3d_client.cc', - 'image_data.cc', - 'input_event.cc', - 'instance.cc', - 'instance_handle.cc', - 'lock.cc', - 'message_loop.cc', - 'module.cc', - 'mouse_cursor.cc', - 'mouse_lock.cc', - 'rect.cc', - 'resource.cc', - 'url_loader.cc', - 'url_request_info.cc', - 'url_response_info.cc', - 'var.cc', - 'var_array_buffer.cc', - 'view.cc', - 'websocket.cc', + # ppapi/cpp + 'array_output.cc', + 'audio.cc', + 'audio_config.cc', + 'core.cc', + 'directory_entry.cc', + 'file_io.cc', + 'file_ref.cc', + 'file_system.cc', + 'fullscreen.cc', + 'graphics_2d.cc', + 'graphics_3d.cc', + 'graphics_3d_client.cc', + 'host_resolver.cc', + 'image_data.cc', + 'input_event.cc', + 'instance.cc', + 'instance_handle.cc', + 'message_loop.cc', + 'module.cc', + 'mouse_cursor.cc', + 'mouse_lock.cc', + 'net_address.cc', + 'network_proxy.cc', + 'ppp_entrypoints.cc', + 'rect.cc', + 'resource.cc', + 'tcp_socket.cc', + 'udp_socket.cc', + 'url_loader.cc', + 'url_request_info.cc', + 'url_response_info.cc', + 'var_array_buffer.cc', + 'var_array.cc', + 'var.cc', + 'var_dictionary.cc', + 'view.cc', + 'websocket.cc', - # Utility sources. - 'paint_aggregator.cc', - 'paint_manager.cc', - 'simple_thread.cc', - 'websocket_api.cc', + # ppapi/cpp/dev + 'widget_client_dev.cc', + 'resource_array_dev.cc', + 'video_capture_client_dev.cc', + 'video_decoder_client_dev.cc', + 'crypto_dev.cc', + 'selection_dev.cc', + 'buffer_dev.cc', + 'url_util_dev.cc', + 'video_capture_dev.cc', + 'zoom_dev.cc', + 'ime_input_event_dev.cc', + 'text_input_dev.cc', + 'font_dev.cc', + 'find_dev.cc', + 'view_dev.cc', + 'memory_dev.cc', + 'truetype_font_dev.cc', + 'file_chooser_dev.cc', + 'video_decoder_dev.cc', + 'cursor_control_dev.cc', + 'device_ref_dev.cc', + 'printing_dev.cc', + 'scriptable_object_deprecated.cc', + 'audio_input_dev.cc', + 'scrollbar_dev.cc', + 'graphics_2d_dev.cc', + 'widget_dev.cc', + + # ppapi/cpp/extensions + 'event_base.cc', + + # ppapi/cpp/extensions/dev + 'alarms_dev.cc', + 'events_dev.cc', + 'socket_dev.cc', + + # ppapi/utility/graphics + 'paint_aggregator.cc', + 'paint_manager.cc', + + # ppapi/utility/websocket + 'websocket_api.cc', + + # ppapi/utility/threading + 'lock.cc', + 'simple_thread.cc', ], } ], + 'HEADERS': [ + { + 'FILES': [ + 'array_output.h', + 'audio_config.h', + 'audio.h', + 'completion_callback.h', + 'core.h', + 'directory_entry.h', + 'file_io.h', + 'file_ref.h', + 'file_system.h', + 'fullscreen.h', + 'graphics_2d.h', + 'graphics_3d_client.h', + 'graphics_3d.h', + 'host_resolver.h', + 'image_data.h', + 'input_event.h', + 'instance.h', + 'instance_handle.h', + 'logging.h', + 'message_loop.h', + 'module_embedder.h', + 'module.h', + 'module_impl.h', + 'mouse_cursor.h', + 'mouse_lock.h', + 'net_address.h', + 'network_proxy.h', + 'output_traits.h', + 'pass_ref.h', + 'point.h', + 'rect.h', + 'resource.h', + 'size.h', + 'tcp_socket.h', + 'touch_point.h', + 'udp_socket.h', + 'url_loader.h', + 'url_request_info.h', + 'url_response_info.h', + 'var_array_buffer.h', + 'var_array.h', + 'var_dictionary.h', + 'var.h', + 'view.h', + 'websocket.h', + ], + 'DEST': 'include/ppapi/cpp', + }, + { + 'FILES': [ + 'audio_input_dev.h', + 'buffer_dev.h', + 'crypto_dev.h', + 'cursor_control_dev.h', + 'device_ref_dev.h', + 'file_chooser_dev.h', + 'find_dev.h', + 'font_dev.h', + 'graphics_2d_dev.h', + 'ime_input_event_dev.h', + 'memory_dev.h', + 'printing_dev.h', + 'resource_array_dev.h', + 'scriptable_object_deprecated.h', + 'scrollbar_dev.h', + 'selection_dev.h', + 'text_input_dev.h', + 'truetype_font_dev.h', + 'url_util_dev.h', + 'video_capture_client_dev.h', + 'video_capture_dev.h', + 'video_decoder_client_dev.h', + 'video_decoder_dev.h', + 'view_dev.h', + 'widget_client_dev.h', + 'widget_dev.h', + 'zoom_dev.h', + ], + 'DEST': 'include/ppapi/cpp/dev', + }, + { + 'FILES': [ + 'dict_field.h', + 'event_base.h', + 'ext_output_traits.h', + 'from_var_converter.h', + 'optional.h', + 'to_var_converter.h', + ], + 'DEST': 'include/ppapi/cpp/extensions', + }, + { + 'FILES': [ + 'alarms_dev.h', + 'events_dev.h', + 'socket_dev.h', + ], + 'DEST': 'include/ppapi/cpp/extensions/dev', + }, + { + 'FILES': [ + 'completion_callback_factory.h', + 'completion_callback_factory_thread_traits.h', + ], + 'DEST': 'include/ppapi/utility', + }, + { + 'FILES': [ + 'paint_aggregator.h', + 'paint_manager.h', + ], + 'DEST': 'include/ppapi/utility/graphics', + }, + { + 'FILES': [ + 'paint_aggregator.h', + 'paint_manager.h', + ], + 'DEST': 'include/ppapi/utility/graphics', + }, + { + 'FILES': [ + 'websocket_api.h', + ], + 'DEST': 'include/ppapi/utility/websocket', + }, + { + 'FILES': [ + 'lock.h', + 'simple_thread.h', + ], + 'DEST': 'include/ppapi/utility/threading', + }, + ], 'DEST': 'src', 'NAME': 'ppapi_cpp', } diff --git a/native_client_sdk/src/libraries/ppapi_cpp_private/library.dsc b/native_client_sdk/src/libraries/ppapi_cpp_private/library.dsc index 8646c3a110..058d79bc5c 100644 --- a/native_client_sdk/src/libraries/ppapi_cpp_private/library.dsc +++ b/native_client_sdk/src/libraries/ppapi_cpp_private/library.dsc @@ -1,6 +1,7 @@ { 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win', 'linux'], 'SEARCH': [ + '../../../../ppapi/c/private', '../../../../ppapi/cpp/private', ], 'TARGETS': [ @@ -20,6 +21,40 @@ ], } ], + 'HEADERS': [ + # ppapi/c/private + { + 'FILES': [ + 'ppb_ext_crx_file_system_private.h', + 'ppb_file_io_private.h', + 'ppb_file_ref_private.h', + 'ppb_host_resolver_private.h', + 'ppb_net_address_private.h', + 'ppb_tcp_server_socket_private.h', + 'ppb_tcp_socket_private.h', + 'ppb_udp_socket_private.h', + 'ppb_x509_certificate_private.h', + 'pp_file_handle.h', + ], + 'DEST': 'include/ppapi/c/private', + }, + + # ppapi/cpp/private + { + 'FILES': [ + 'ext_crx_file_system_private.h', + 'file_io_private.h', + 'host_resolver_private.h', + 'net_address_private.h', + 'pass_file_handle.h', + 'tcp_server_socket_private.h', + 'tcp_socket_private.h', + 'udp_socket_private.h', + 'x509_certificate_private.h', + ], + 'DEST': 'include/ppapi/cpp/private', + }, + ], 'DEST': 'src', 'NAME': 'ppapi_cpp_private', } diff --git a/native_client_sdk/src/libraries/ppapi_gles2/library.dsc b/native_client_sdk/src/libraries/ppapi_gles2/library.dsc index ccf23bd769..bddf8c66b1 100644 --- a/native_client_sdk/src/libraries/ppapi_gles2/library.dsc +++ b/native_client_sdk/src/libraries/ppapi_gles2/library.dsc @@ -1,8 +1,10 @@ { 'TOOLS': ['newlib', 'glibc', 'pnacl', 'linux', 'win'], 'SEARCH' : [ - '.', - '../../../../ppapi/lib/gl/gles2' + '../../../../ppapi/lib/gl/gles2', + '../../../../ppapi/lib/gl/include/EGL', + '../../../../ppapi/lib/gl/include/GLES2', + '../../../../ppapi/lib/gl/include/KHR' ], 'TARGETS': [ { @@ -14,6 +16,38 @@ ], } ], + 'HEADERS': [ + # ppapi/lib/gl/include/KHR + { + 'FILES': [ + 'khrplatform.h' + ], + 'DEST': 'include/KHR', + }, + + # ppapi/lib/gl/include/GLES2 + { + 'FILES': [ + 'gl2.h', + 'gl2ext.h', + 'gl2platform.h', + ], + 'DEST': 'include/GLES2', + }, + + # ppapi/lib/gl/gles2 + { + 'FILES': [ 'gl2ext_ppapi.h' ], + 'DEST': 'include/ppapi/gles2', + }, + + # Create a duplicate copy of this header + # TODO(sbc), remove this copy once we find a way to build gl2ext_ppapi.c. + { + 'FILES': [ 'gl2ext_ppapi.h' ], + 'DEST': 'include/ppapi/lib/gl/gles2', + }, + ], 'DEST': 'src', 'NAME': 'ppapi_gles2', } diff --git a/native_client_sdk/src/libraries/ppapi_simple/library.dsc b/native_client_sdk/src/libraries/ppapi_simple/library.dsc index 613a585227..841aeedf8f 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/library.dsc +++ b/native_client_sdk/src/libraries/ppapi_simple/library.dsc @@ -1,13 +1,15 @@ { - 'TOOLS': ['newlib', 'glibc', 'pnacl'], + 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win'], 'TARGETS': [ { 'NAME' : 'ppapi_simple', 'TYPE' : 'lib', 'SOURCES' : [ "ps.cc", + "ps_context_2d.cc", "ps_event.cc", "ps_instance.cc", + "ps_interface.cc", "ps_main.cc", ], } @@ -16,8 +18,10 @@ { 'FILES': [ "ps.h", + "ps_context_2d.h", "ps_event.h", "ps_instance.h", + "ps_interface.h", "ps_main.h", ], 'DEST': 'include/ppapi_simple', diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps.h b/native_client_sdk/src/libraries/ppapi_simple/ps.h index 1d7b2543ae..fe5205b36a 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/ps.h +++ b/native_client_sdk/src/libraries/ppapi_simple/ps.h @@ -85,12 +85,34 @@ extern void* PSUserCreateInstance(PP_Instance inst); * configuration information and optional extra arguments which are passed to * the 'main' function. See the appropriate ppapi_simple_main(XX).h header. */ +#if defined(WIN32) + +/* In MSVC, ##__VA_ARGS does not remove the following comma, only the + * previous one. As a result, passing no extra arguments to + * PPAPI_SIMPLE_USE_MAIN yields: + * + * static const char* params[] = { , NULL, NULL }; + * + * We work around this by always preceding it with a "NULL,". That way the + * previous comma is removed and the following comma takes its place. We then + * skip this initial bogus value when passing the array to the factory. + */ +#define PPAPI_SIMPLE_USE_MAIN(factory, func, ...) \ +void* PSUserCreateInstance(PP_Instance inst) { \ + PPAPI_SIMPLE_DECLARE_PARAMS(params, NULL, ##__VA_ARGS__, NULL, NULL) \ + return factory(inst, func, params + 1); \ +} + +#else + #define PPAPI_SIMPLE_USE_MAIN(factory, func, ...) \ void* PSUserCreateInstance(PP_Instance inst) { \ PPAPI_SIMPLE_DECLARE_PARAMS(params, ##__VA_ARGS__, NULL, NULL) \ return factory(inst, func, params); \ } +#endif + /** * PPAPI_SIMPLE_USE_CLASS @@ -109,5 +131,4 @@ void* PSUserCreateInstance(PP_Instance inst) { \ EXTERN_C_END -#endif // PPAPI_SIMPLE_PPAPI_SIMPLE_H - +#endif // PPAPI_SIMPLE_PS_H_ diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.cc b/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.cc new file mode 100644 index 0000000000..743dabea96 --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.cc @@ -0,0 +1,134 @@ +// Copyright 2013 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.auto + +#include <stdlib.h> +#include <string.h> + +#include "ppapi/c/pp_rect.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/pp_size.h" +#include "ppapi/c/ppb_core.h" +#include "ppapi/c/ppb_graphics_2d.h" +#include "ppapi/c/ppb_image_data.h" +#include "ppapi/c/ppb_instance.h" +#include "ppapi/c/ppb_view.h" +#include "ppapi/cpp/image_data.h" + +#include "ppapi_simple/ps.h" +#include "ppapi_simple/ps_context_2d.h" +#include "ppapi_simple/ps_event.h" +#include "ppapi_simple/ps_instance.h" +#include "ppapi_simple/ps_interface.h" + +PSContext2D_t* PSContext2DAllocate() { + PSContext2D_t* ctx = (PSContext2D_t*) malloc(sizeof(PSContext2D_t)); + memset(ctx, 0, sizeof(PSContext2D_t)); + + ctx->format = PSInterfaceImageData()->GetNativeImageDataFormat(); + return ctx; +} + +void PSContext2DFree(PSContext2D_t* ctx) { + if (ctx->graphic_2d) { + PSInterfaceCore()->ReleaseResource(ctx->graphic_2d); + ctx->graphic_2d = 0; + } + if (ctx->image) { + PSInterfaceCore()->ReleaseResource(ctx->image); + ctx->image = 0; + } + free(ctx); +} + +// Update the 2D context if the message is appropriate, returning non-zero +// if the event was consumed. +int PSContext2DHandleEvent(PSContext2D_t* ctx, PSEvent* event) { + switch(event->type) { + case PSE_INSTANCE_DIDCHANGEVIEW: { + struct PP_Rect rect; + + PSInterfaceView()->GetRect(event->as_resource, &rect); + PSInterfaceCore()->ReleaseResource(ctx->graphic_2d); + ctx->bound = 0; + ctx->width = rect.size.width; + ctx->height = rect.size.height; + + // Create an opaque graphic context of the specified size. + ctx->graphic_2d = + PSInterfaceGraphics2D()->Create(PSGetInstanceId(), &rect.size, + PP_TRUE); + + // Bind the context to so that draws will be visible. + if (ctx->graphic_2d) { + ctx->bound = + PSInterfaceInstance()->BindGraphics(PSGetInstanceId(), + ctx->graphic_2d); + } + + // Typically this resource would not be allocated yet, but just in case + // throw it away, to force a new allocation when GetBuffer is called. + if (ctx->image) { + PSInterfaceCore()->ReleaseResource(ctx->image); + ctx->image = 0; + } + + return 1; + } + default: break; + } + + return 0; +} + + +// Allocates (if needed) a new image context which will be swapped in when +// drawing is complete. PSContextGetBuffer and PSContext2DSwapBuffer +// implemented the suggested image/graphic_2d use specified in the +// ppb_graphics_2d header. +int PSContext2DGetBuffer(PSContext2D_t* ctx) { + if (!ctx->bound) return 0; + + // Check if we are already holding an image + if (ctx->image) return 1; + + PP_Size size; + size.width = ctx->width; + size.height = ctx->height; + + // Allocate a new image resource with the specified size and format, but + // do not ZERO out the buffer first since we will fill it. + PP_Resource image = + PSInterfaceImageData()->Create(PSGetInstanceId(), ctx->format, &size, + PP_FALSE); + + if (0 == image) { + PSInstance::GetInstance()->Error("Unable to create 2D image.\n"); + return 0; + } + + // Get the stride + struct PP_ImageDataDesc desc; + PSInterfaceImageData()->Describe(image, &desc); + + ctx->image = image; + ctx->data = static_cast<uint32_t*>(PSInterfaceImageData()->Map(image)); + ctx->stride = desc.stride; + return 1; +} + +int PSContext2DSwapBuffer(PSContext2D_t* ctx) { + if (ctx->bound && ctx->image) { + PSInterfaceImageData()->Unmap(ctx->image); + PSInterfaceGraphics2D()->ReplaceContents(ctx->graphic_2d, ctx->image); + PSInterfaceGraphics2D()->Flush(ctx->graphic_2d, PP_BlockUntilComplete()); + PSInterfaceCore()->ReleaseResource(ctx->image); + + ctx->image = 0; + ctx->stride = 0; + ctx->data = NULL; + return 1; + } + return 0; +} + diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.h b/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.h new file mode 100644 index 0000000000..30d0cf9cc7 --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.h @@ -0,0 +1,67 @@ +/* Copyright 2013 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. */ + +#ifndef PPAPI_SIMPLE_PS_CONTEXT_2D_H_ +#define PPAPI_SIMPLE_PS_CONTEXT_2D_H_ + +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/ppb_graphics_2d.h" +#include "ppapi/c/ppb_image_data.h" + +#include "ppapi_simple/ps_event.h" + +#include "sdk_util/macros.h" + +EXTERN_C_BEGIN + +typedef struct { + int bound; + int32_t width; + int32_t height; + uint32_t stride; + uint32_t* data; + PP_ImageDataFormat format; + PP_Resource graphic_2d; + PP_Resource image; +} PSContext2D_t; + + +/* + * PSContext2DAllocate + * + * Allocate or free a 2D context object which the library can use to perform + * various PPAPI operations on the developer's behalf, such as processing view + * change events, swapping buffers, etc... + */ +PSContext2D_t* PSContext2DAllocate(); +void PSContext2DFree(PSContext2D_t* ctx); + +/* + * PSContext2DHandleEvent + * + * Updates the context such as allocating, freeing, or sizing graphics and + * image resources in response to events. + */ +int PSContext2DHandleEvent(PSContext2D_t* ctx, PSEvent* event); + +/* + * PSContext2DGetBuffer + * + * Points the data member of the context to the raw pixels of the image for + * writing to the screen. The image will become visible after a swap. + */ +int PSContext2DGetBuffer(PSContext2D_t* ctx); + +/* + * PSContext2DSwapBuffer + * + * Swaps out the currently visible graphics with the data stored in the image + * buffer making it visible. The old image resource will no longer be + * available after this call. + */ +int PSContext2DSwapBuffer(PSContext2D_t* ctx); + +EXTERN_C_END + +#endif /* PPAPI_SIMPLE_PS_CONTEXT_2D_H_ */ diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_event.h b/native_client_sdk/src/libraries/ppapi_simple/ps_event.h index 563832c703..731f053b13 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/ps_event.h +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_event.h @@ -1,7 +1,6 @@ -// Copyright (c) 2013 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. - +/* Copyright (c) 2013 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. */ #ifndef PPAPI_SIMPLE_PS_EVENT_H_ #define PPAPI_SIMPLE_PS_EVENT_H_ @@ -91,5 +90,4 @@ void PSEventPostResource(PSEventType type, PP_Resource resource); EXTERN_C_END -#endif // PPAPI_SIMPLE_PS_EVENT_H_ - +#endif /* PPAPI_SIMPLE_PS_EVENT_H_ */ diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc index 251876430b..4612a52cfd 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <errno.h> #include <fcntl.h> #include <pthread.h> -#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> @@ -16,6 +16,7 @@ #include <string> #include <vector> +#include "nacl_io/ioctl.h" #include "nacl_io/kernel_wrap.h" #include "nacl_io/nacl_io.h" @@ -31,8 +32,14 @@ #include "ppapi_simple/ps_event.h" #include "ppapi_simple/ps_instance.h" +#include "ppapi_simple/ps_interface.h" #include "ppapi_simple/ps_main.h" +#if defined(WIN32) +#define open _open +#define dup2 _dup2 +#endif + static PSInstance* s_InstanceObject = NULL; PSInstance* PSInstance::GetInstance() { @@ -49,7 +56,7 @@ struct StartInfo { // The starting point for 'main'. We create this thread to hide the real // main pepper thread which must never be blocked. void* PSInstance::MainThreadThunk(void *info) { - s_InstanceObject->Log("Got MainThreadThunk.\n"); + s_InstanceObject->Trace("Got MainThreadThunk.\n"); StartInfo* si = static_cast<StartInfo*>(info); si->inst_->main_loop_ = new pp::MessageLoop(si->inst_); si->inst_->main_loop_->AttachToCurrentThread(); @@ -61,31 +68,38 @@ void* PSInstance::MainThreadThunk(void *info) { delete[] si->argv_; delete si; + // Exit the entire process once the 'main' thread returns. + // The error code will be available to javascript via + // the exitcode paramater of the crash event. + exit(ret); return NULL; } // The default implementation supports running a 'C' main. int PSInstance::MainThread(int argc, char *argv[]) { - if (main_cb_) { - Log("Starting MAIN.\n"); - int ret = main_cb_(argc, argv); - Log("Main thread returned with %d.\n", ret); - return ret; + if (!main_cb_) { + Error("No main defined.\n"); + return 0; } - Error("No main defined.\n"); - return 0; + Trace("Starting MAIN.\n"); + int ret = main_cb_(argc, argv); + Log("Main thread returned with %d.\n", ret); + return ret; } PSInstance::PSInstance(PP_Instance instance, const char *argv[]) : pp::Instance(instance), + pp::MouseLock(this), + pp::Graphics3DClient(this), main_loop_(NULL), - verbosity_(PSV_LOG), - events_enabled_(PSE_NONE) { + verbosity_(PSV_WARN), + events_enabled_(PSE_NONE), + fd_tty_(-1) { // Set the single Instance object s_InstanceObject = this; -#ifdef DEBUG +#ifdef NACL_SDK_DEBUG SetVerbosity(PSV_LOG); #endif @@ -100,15 +114,6 @@ PSInstance::PSInstance(PP_Instance instance, const char *argv[]) PP_INPUTEVENT_CLASS_KEYBOARD | PP_INPUTEVENT_CLASS_WHEEL | PP_INPUTEVENT_CLASS_TOUCH); - - ppb_core_ = static_cast<const PPB_Core*>( - pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE)); - - ppb_var_ = static_cast<const PPB_Var*>( - pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE)); - - ppb_view_ = static_cast<const PPB_View*>( - pp::Module::Get()->GetBrowserInterface(PPB_VIEW_INTERFACE)); } PSInstance::~PSInstance() {} @@ -128,13 +133,7 @@ bool PSInstance::Init(uint32_t arg, si->argv_[0] = NULL; // Process arguments passed into Module INIT from JavaScript - for (uint32_t i=0; i < arg; i++) { - if (argv[i]) { - Log("ARG %s=%s\n", argn[i], argv[i]); - } else { - Log("ARG %s\n", argn[i]); - } - + for (uint32_t i = 0; i < arg; i++) { // If we start with PM prefix set the instance argument map if (0 == strncmp(argn[i], "ps_", 3)) { std::string key = argn[i]; @@ -143,25 +142,33 @@ bool PSInstance::Init(uint32_t arg, continue; } + // Chrome passed @dev as an internal extra attribute in + // some cases. Ignore this. + if (0 == strcmp(argn[i], "@dev")) { + continue; + } + // If this is the 'src' tag, then get the NMF name. if (!strcmp("src", argn[i])) { char *name = new char[strlen(argv[i]) + 1]; strcpy(name, argv[i]); si->argv_[0] = name; - } - else { - // Otherwise turn it into arguments - char *key = new char[strlen(argn[i]) + 3]; - key[0] = '-'; - key[1] = '-'; - strcpy(&key[2], argn[i]); - - si->argv_[si->argc_++] = key; - if (argv[i] && argv[i][0]) { - char *val = new char[strlen(argv[i]) + 1]; - strcpy(val, argv[i]); - si->argv_[si->argc_++] = val; - } + } else { + // Otherwise turn html tag attributes into arguments + // that get passed to the main funciton. Attributes + // without values get transformed into "--name" and + // attributes with values become "--name=value". + int arglen = strlen(argn[i]) + 3; + if (argv[i] && argv[i][0]) + arglen += strlen(argv[i]) + 1; + + char* arg = new char[arglen]; + if (argv[i] && argv[i][0]) + sprintf(arg, "--%s=%s", argn[i], argv[i]); + else + sprintf(arg, "--%s", argn[i]); + + si->argv_[si->argc_++] = arg; } } @@ -172,15 +179,33 @@ bool PSInstance::Init(uint32_t arg, si->argv_[0] = name; } - if (ProcessProperties()) { - pthread_t main_thread; - int ret = pthread_create(&main_thread, NULL, MainThreadThunk, si); - Log("Created thread: %d.\n", ret); - return ret == 0; + PSInterfaceInit(); + bool props_processed = ProcessProperties(); + + // Log arg values only once ProcessProperties has been + // called so that the ps_verbosity attribute will be in + // effect. + for (uint32_t i = 0; i < arg; i++) { + if (argv[i]) { + Trace("attribs[%d] '%s=%s'\n", i, argn[i], argv[i]); + } else { + Trace("attribs[%d] '%s'\n", i, argn[i]); + } } - Log("Skipping create thread.\n"); - return false; + for (uint32_t i = 0; i < si->argc_; i++) { + Trace("argv[%d] '%s'\n", i, si->argv_[i]); + } + + if (!props_processed) { + Warn("Skipping create thread.\n"); + return false; + } + + pthread_t main_thread; + int ret = pthread_create(&main_thread, NULL, MainThreadThunk, si); + Trace("Created thread: %d.\n", ret); + return ret == 0; } const char* PSInstance::GetProperty(const char* key, const char* def) { @@ -191,7 +216,7 @@ const char* PSInstance::GetProperty(const char* key, const char* def) { return def; } -// Processes the properties passed fixed at compile time via the +// Processes the properties set at compile time via the // initialization macro, or via dynamically set embed attributes // through instance DidCreate. bool PSInstance::ProcessProperties() { @@ -200,6 +225,7 @@ bool PSInstance::ProcessProperties() { const char* stdout_path = GetProperty("ps_stdout", "/dev/stdout"); const char* stderr_path = GetProperty("ps_stderr", "/dev/console3"); const char* verbosity = GetProperty("ps_verbosity", NULL); + const char* tty_prefix = GetProperty("ps_tty_prefix", NULL); // Reset verbosity if passed in if (verbosity) SetVerbosity(static_cast<Verbosity>(atoi(verbosity))); @@ -215,9 +241,20 @@ bool PSInstance::ProcessProperties() { int fd2 = open(stderr_path, O_WRONLY); dup2(fd2, 2); + if (tty_prefix) { + fd_tty_ = open("/dev/tty", O_WRONLY); + if (fd_tty_ >= 0) { + ioctl(fd_tty_, TIOCNACLPREFIX, const_cast<char*>(tty_prefix)); + } else { + Error("Failed to open /dev/tty.\n"); + } + } + // Set line buffering on stdout and stderr +#if !defined(WIN32) setvbuf(stderr, NULL, _IOLBF, 0); setvbuf(stdout, NULL, _IOLBF, 0); +#endif return true; } @@ -225,28 +262,39 @@ void PSInstance::SetVerbosity(Verbosity verbosity) { verbosity_ = verbosity; } -void PSInstance::Log(const char *fmt, ...) { - if (verbosity_ >= PSV_LOG) { - va_list ap; - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); +void PSInstance::VALog(Verbosity verbosity, const char *fmt, va_list args) { + if (verbosity <= verbosity_) { + fprintf(stderr, "ps: "); + vfprintf(stderr, fmt, args); } } +void PSInstance::Trace(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + VALog(PSV_TRACE, fmt, ap); + va_end(ap); +} + +void PSInstance::Log(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + VALog(PSV_LOG, fmt, ap); + va_end(ap); +} + void PSInstance::Warn(const char *fmt, ...) { - if (verbosity_ >= PSV_WARN) { - va_list ap; - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - } + va_list ap; + va_start(ap, fmt); + VALog(PSV_WARN, fmt, ap); + va_end(ap); } void PSInstance::Error(const char *fmt, ...) { - if (verbosity_ >= PSV_ERROR) { - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - } + va_list ap; + va_start(ap, fmt); + VALog(PSV_ERROR, fmt, ap); + va_end(ap); } void PSInstance::SetEnabledEvents(uint32_t mask) { @@ -283,7 +331,7 @@ void PSInstance::PostEvent(PSEventType type, PP_Resource resource) { if (events_enabled_ & type) { if (resource) { - ppb_core_->AddRefResource(resource); + PSInterfaceCore()->AddRefResource(resource); } PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); memset(env, 0, sizeof(*env)); @@ -296,8 +344,32 @@ void PSInstance::PostEvent(PSEventType type, PP_Resource resource) { void PSInstance::PostEvent(PSEventType type, const PP_Var& var) { assert(PSE_INSTANCE_HANDLEMESSAGE == type); + // If the user has specified a tty_prefix_ (using ioctl), then we'll give the + // tty node a chance to vacuum up any messages beginning with that prefix. If + // the message does not start with the prefix, the ioctl call will return + // ENOENT and we'll pass the message through to the event queue. + if (fd_tty_ >= 0 && var.type == PP_VARTYPE_STRING) { + uint32_t message_len; + const char* message = PSInterfaceVar()->VarToUtf8(var, &message_len); + std::string message_str(message, message + message_len); + + // Since our message may contain null characters, we can't send it as a + // naked C string, so we package it up in this struct before sending it + // to the ioctl. + struct tioc_nacl_input_string ioctl_message; + ioctl_message.length = message_len; + ioctl_message.buffer = message_str.data(); + int ret = + ioctl(fd_tty_, TIOCNACLINPUT, reinterpret_cast<char*>(&ioctl_message)); + if (ret != ENOTTY) { + Error("ioctl returned unexpected error: %d.\n", ret); + } + + return; + } + if (events_enabled_ & type) { - ppb_var_->AddRef(var); + PSInterfaceVar()->AddRef(var); PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); memset(env, 0, sizeof(*env)); env->type = type; @@ -318,12 +390,12 @@ void PSInstance::ReleaseEvent(PSEvent* event) { if (event) { switch(event->type) { case PSE_INSTANCE_HANDLEMESSAGE: - ppb_var_->Release(event->as_var); + PSInterfaceVar()->Release(event->as_var); break; case PSE_INSTANCE_HANDLEINPUT: case PSE_INSTANCE_DIDCHANGEVIEW: if (event->as_resource) { - ppb_core_->ReleaseResource(event->as_resource); + PSInterfaceCore()->ReleaseResource(event->as_resource); } break; default: @@ -334,12 +406,11 @@ void PSInstance::ReleaseEvent(PSEvent* event) { } void PSInstance::HandleMessage(const pp::Var& message) { - Log("Got Message\n"); + Trace("Got Message\n"); PostEvent(PSE_INSTANCE_HANDLEMESSAGE, message.pp_var()); } bool PSInstance::HandleInputEvent(const pp::InputEvent& event) { - Log("Got Input\n"); PostEvent(PSE_INSTANCE_HANDLEINPUT, event.pp_resource()); return true; } @@ -355,3 +426,12 @@ void PSInstance::DidChangeFocus(bool focus) { PostEvent(PSE_INSTANCE_DIDCHANGEFOCUS, focus ? PP_TRUE : PP_FALSE); } +void PSInstance::Graphics3DContextLost() { + Log("Graphics3DContextLost\n"); + PostEvent(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST); +} + +void PSInstance::MouseLockLost() { + Log("MouseLockLost\n"); + PostEvent(PSE_MOUSELOCK_MOUSELOCKLOST); +} diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h index bf9fbe4a9e..f02d574d5c 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h @@ -5,6 +5,7 @@ #ifndef PPAPI_SIMPLE_PS_INSTANCE_H_ #define PPAPI_SIMPLE_PS_INSTANCE_H_ +#include <stdarg.h> #include <map> #include "ppapi/c/pp_instance.h" @@ -14,6 +15,7 @@ #include "ppapi/c/ppb_view.h" #include "ppapi/cpp/fullscreen.h" +#include "ppapi/cpp/graphics_3d_client.h" #include "ppapi/cpp/instance.h" #include "ppapi/cpp/message_loop.h" #include "ppapi/cpp/mouse_lock.h" @@ -28,13 +30,18 @@ typedef std::map<std::string, std::string> PropertyMap_t; -class PSInstance : public pp::Instance { +// The basic instance class which also inherits the MouseLock and +// Graphics3DClient interfaces. +class PSInstance : public pp::Instance, pp::MouseLock, pp::Graphics3DClient { public: + // Verbosity levels, ecplicitly numbered since we pass these + // in from html attributes as numberic values. enum Verbosity { - PSV_SILENT, - PSV_ERROR, - PSV_WARN, - PSV_LOG, + PSV_SILENT = 0, + PSV_ERROR = 1, + PSV_WARN = 2, + PSV_LOG = 3, + PSV_TRACE = 4, }; // Returns a pointer to the global instance @@ -56,6 +63,7 @@ class PSInstance : public pp::Instance { // Logging Functions void SetVerbosity(Verbosity verbosity); + void Trace(const char *fmt, ...); void Log(const char *fmt, ...); void Warn(const char *fmt, ...); void Error(const char *fmt, ...); @@ -81,6 +89,10 @@ class PSInstance : public pp::Instance { // This function will create a new thread which will run the pseudo main. virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); + // Output log message to stderr if the current verbosity is set + // at or above the given verbosity. + void VALog(Verbosity verbosity, const char *fmt, va_list args); + // Called whenever the in-browser window changes size, it will pass a // context change request to whichever thread is handling rendering. virtual void DidChangeView(const pp::View& view); @@ -95,6 +107,12 @@ class PSInstance : public pp::Instance { // and can later be processed on a sperate processing thread. virtual bool HandleInputEvent(const pp::InputEvent& event); + // Called by the browser when the 3D context is lost. + virtual void Graphics3DContextLost(); + + // Called by the browser when the mouselock is lost. + virtual void MouseLockLost(); + // Called by Init to processes default and embed tag arguments prior to // launching the 'ppapi_main' thread. virtual bool ProcessProperties(); @@ -109,13 +127,16 @@ class PSInstance : public pp::Instance { ThreadSafeQueue<PSEvent> event_queue_; uint32_t events_enabled_; Verbosity verbosity_; + int fd_tty_; PSMainFunc_t main_cb_; const PPB_Core* ppb_core_; const PPB_Var* ppb_var_; const PPB_View* ppb_view_; + + friend class PSGraphics3DClient; + friend class PSMouseLock; }; #endif // PPAPI_MAIN_PS_INSTANCE_H_ - diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_interface.cc b/native_client_sdk/src/libraries/ppapi_simple/ps_interface.cc new file mode 100644 index 0000000000..43988e2996 --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_interface.cc @@ -0,0 +1,64 @@ +// Copyright 2013 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. + +#include "ppapi_simple/ps.h" +#include "ppapi_simple/ps_interface.h" + +#define DEFINE_INTERFACE_FUNC(Name) \ + static const PPB_##Name* s_##Name; \ + const PPB_##Name* PSInterface##Name() { return s_##Name; } + +DEFINE_INTERFACE_FUNC(Audio) +DEFINE_INTERFACE_FUNC(AudioConfig) +DEFINE_INTERFACE_FUNC(Console) +DEFINE_INTERFACE_FUNC(Core) +DEFINE_INTERFACE_FUNC(FileIO) +DEFINE_INTERFACE_FUNC(FileRef) +DEFINE_INTERFACE_FUNC(FileSystem) +DEFINE_INTERFACE_FUNC(Fullscreen) +DEFINE_INTERFACE_FUNC(Gamepad) +DEFINE_INTERFACE_FUNC(Graphics2D) +DEFINE_INTERFACE_FUNC(Graphics3D) +DEFINE_INTERFACE_FUNC(ImageData) +DEFINE_INTERFACE_FUNC(Instance) +DEFINE_INTERFACE_FUNC(Messaging) +DEFINE_INTERFACE_FUNC(MessageLoop) +DEFINE_INTERFACE_FUNC(MouseCursor) +DEFINE_INTERFACE_FUNC(URLLoader) +DEFINE_INTERFACE_FUNC(URLRequestInfo) +DEFINE_INTERFACE_FUNC(URLResponseInfo) +DEFINE_INTERFACE_FUNC(Var) +DEFINE_INTERFACE_FUNC(VarArrayBuffer) +DEFINE_INTERFACE_FUNC(View) +DEFINE_INTERFACE_FUNC(WebSocket) + + +#define REQUEST_INTERFACE(x, y) \ + s_##x = static_cast<const PPB_##x*>(PSGetInterface(PPB_ ## y ##_INTERFACE)); + +void PSInterfaceInit() { + REQUEST_INTERFACE(Audio, AUDIO) + REQUEST_INTERFACE(AudioConfig, AUDIO_CONFIG) + REQUEST_INTERFACE(Console, CONSOLE) + REQUEST_INTERFACE(Core, CORE) + REQUEST_INTERFACE(FileIO, FILEIO) + REQUEST_INTERFACE(FileRef, FILEREF) + REQUEST_INTERFACE(FileSystem, FILESYSTEM) + REQUEST_INTERFACE(Fullscreen, FULLSCREEN) + REQUEST_INTERFACE(Gamepad, GAMEPAD) + REQUEST_INTERFACE(Graphics2D, GRAPHICS_2D) + REQUEST_INTERFACE(Graphics3D, GRAPHICS_3D) + REQUEST_INTERFACE(ImageData, IMAGEDATA) + REQUEST_INTERFACE(Instance, INSTANCE) + REQUEST_INTERFACE(Messaging, MESSAGING) + REQUEST_INTERFACE(MessageLoop, MESSAGELOOP) + REQUEST_INTERFACE(MouseCursor, MOUSECURSOR) + REQUEST_INTERFACE(URLLoader, URLLOADER) + REQUEST_INTERFACE(URLRequestInfo, URLREQUESTINFO) + REQUEST_INTERFACE(URLResponseInfo, URLRESPONSEINFO) + REQUEST_INTERFACE(Var, VAR) + REQUEST_INTERFACE(VarArrayBuffer, VAR_ARRAY_BUFFER) + REQUEST_INTERFACE(View, VIEW) + REQUEST_INTERFACE(WebSocket, WEBSOCKET) +} diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_interface.h b/native_client_sdk/src/libraries/ppapi_simple/ps_interface.h new file mode 100644 index 0000000000..ad818f06e5 --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_interface.h @@ -0,0 +1,72 @@ +/* Copyright 2013 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. */ + +#ifndef PPAPI_SIMPLE_PS_INTERFACE_H_ +#define PPAPI_SIMPLE_PS_INTERFACE_H_ + +#include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/pp_var.h" + + +#include "ppapi/c/ppb_audio.h" +#include "ppapi/c/ppb_audio_config.h" +#include "ppapi/c/ppb_console.h" +#include "ppapi/c/ppb_core.h" +#include "ppapi/c/ppb_file_io.h" +#include "ppapi/c/ppb_file_ref.h" +#include "ppapi/c/ppb_file_system.h" +#include "ppapi/c/ppb_fullscreen.h" +#include "ppapi/c/ppb_gamepad.h" +#include "ppapi/c/ppb_graphics_2d.h" +#include "ppapi/c/ppb_graphics_3d.h" +#include "ppapi/c/ppb_image_data.h" +#include "ppapi/c/ppb_instance.h" +#include "ppapi/c/ppb_message_loop.h" +#include "ppapi/c/ppb_messaging.h" +#include "ppapi/c/ppb_mouse_cursor.h" +#include "ppapi/c/ppb_mouse_lock.h" +#include "ppapi/c/ppb_url_loader.h" +#include "ppapi/c/ppb_url_request_info.h" +#include "ppapi/c/ppb_url_response_info.h" +#include "ppapi/c/ppb_var.h" +#include "ppapi/c/ppb_var_array_buffer.h" +#include "ppapi/c/ppb_view.h" +#include "ppapi/c/ppb_websocket.h" + +#include "sdk_util/macros.h" + +EXTERN_C_BEGIN + +const PPB_Audio* PSInterfaceAudio(); +const PPB_AudioConfig* PSInterfaceAudioConfig(); +const PPB_Console* PSInterfaceConsole(); +const PPB_Core* PSInterfaceCore(); +const PPB_FileIO* PSInterfaceFileIO(); +const PPB_FileRef* PSInterfaceFileRef(); +const PPB_FileSystem* PSInterfaceFileSystem(); +const PPB_Fullscreen* PSInterfaceFullscreen(); +const PPB_Gamepad* PSInterfaceGamepad(); +const PPB_Graphics2D* PSInterfaceGraphics2D(); +const PPB_Graphics3D* PSInterfaceGraphics3D(); +const PPB_ImageData* PSInterfaceImageData(); +const PPB_Instance* PSInterfaceInstance(); +const PPB_Messaging* PSInterfaceMessaging(); +const PPB_MessageLoop* PSInterfaceMessageLoop(); +const PPB_MouseCursor* PSInterfaceMouseCursor(); +const PPB_URLLoader* PSInterfaceURLLoader(); +const PPB_URLRequestInfo* PSInterfaceURLRequestInfo(); +const PPB_URLResponseInfo* PSInterfaceURLResponseInfo(); +const PPB_Var* PSInterfaceVar(); +const PPB_VarArrayBuffer* PSInterfaceVarArrayBuffer(); +const PPB_View* PSInterfaceView(); +const PPB_WebSocket* PSInterfaceWebSocket(); + + +/* Initializes the Interface module which fetches the above interfaces. */ +void PSInterfaceInit(); + +EXTERN_C_END + +#endif /* PPAPI_SIMPLE_PS_INTERFACE_H */ diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_main.h b/native_client_sdk/src/libraries/ppapi_simple/ps_main.h index c37ac5dba3..9e18edf9e4 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/ps_main.h +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_main.h @@ -1,6 +1,6 @@ -// Copyright (c) 2012 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. +/* Copyright (c) 2012 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. */ #ifndef PPAPI_SIMPLE_PS_MAIN_H_ #define PPAPI_SIMPLE_PS_MAIN_H_ @@ -34,5 +34,4 @@ void* PSMainCreate(PP_Instance inst, PSMainFunc_t func, const char **argv); EXTERN_C_END -#endif /* PPAPI_SIMPLE_PPAPI_SIMPLE_MAIN_H_ */ - +#endif /* PPAPI_SIMPLE_PS_MAIN_H_ */ diff --git a/native_client_sdk/src/libraries/sdk_util/atomicops.h b/native_client_sdk/src/libraries/sdk_util/atomicops.h new file mode 100644 index 0000000000..80d62d2157 --- /dev/null +++ b/native_client_sdk/src/libraries/sdk_util/atomicops.h @@ -0,0 +1,44 @@ +/* Copyright 2013 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. + */ + +#ifndef LIBRARIES_SDK_UTIL_ATOMICOPS_H_ +#define LIBRARIES_SDK_UTIL_ATOMICOPS_H_ + +#ifndef WIN32 + +#include <stdint.h> +typedef int32_t Atomic32; + +#ifndef __llvm__ +static inline void MemoryBarrier() { + __sync_synchronize(); +} +#endif + +inline Atomic32 AtomicAddFetch(volatile Atomic32* ptr, Atomic32 value) { + return __sync_add_and_fetch(ptr, value); +} + +#else + +#include <windows.h> + +/* Undefine many Windows.h macros that we almost certainly do not want. */ +#undef min +#undef max +#undef PostMessage +#undef interface + +typedef long Atomic32; + +/* Windows.h already defines a MemoryBarrier macro. */ + +inline Atomic32 AtomicAddFetch(volatile Atomic32* ptr, Atomic32 value) { + return InterlockedExchangeAdd(ptr, value); +} +#endif + + +#endif /* LIBRARIES_SDK_UTIL_ATOMICOPS_H_ */ diff --git a/native_client_sdk/src/libraries/sdk_util/library.dsc b/native_client_sdk/src/libraries/sdk_util/library.dsc index 0491d980ea..71a51481d7 100644 --- a/native_client_sdk/src/libraries/sdk_util/library.dsc +++ b/native_client_sdk/src/libraries/sdk_util/library.dsc @@ -15,9 +15,11 @@ 'HEADERS': [ { 'FILES': [ + 'atomicops.h', 'auto_lock.h', 'macros.h', 'ref_object.h', + 'scoped_ref.h', 'thread_pool.h', 'thread_safe_queue.h' ], diff --git a/native_client_sdk/src/libraries/sdk_util/ref_object.h b/native_client_sdk/src/libraries/sdk_util/ref_object.h index 4e81f32df6..e446e80141 100644 --- a/native_client_sdk/src/libraries/sdk_util/ref_object.h +++ b/native_client_sdk/src/libraries/sdk_util/ref_object.h @@ -9,21 +9,48 @@ #include <stdlib.h> #include "pthread.h" +#include "sdk_util/atomicops.h" + +class ScopedRefBase; + +/* + * RefObject + * + * A reference counted object with a lock. The lock protects the data within + * the object, but not the reference counting which happens through atomic + * operations. RefObjects should only be handled by ScopedRef objects. + * + * When the object is first created, it has a reference count of zero. It's + * first incremented when it gets assigned to a ScopedRef. When the last + * ScopedRef is reset or destroyed the object will get released. + */ + class RefObject { public: RefObject() { - ref_count_ = 1; + ref_count_ = 0; pthread_mutex_init(&lock_, NULL); } + /* + * RefCount + * + * RefCount returns an instantaneous snapshot of the RefCount, which may + * change immediately after it is fetched. This is only stable when all + * pointers to the object are scoped pointers (ScopedRef), and the value + * is one implying the current thread is the only one, with visibility to + * the object. + */ int RefCount() const { return ref_count_; } + protected: void Acquire() { - ref_count_++; + AtomicAddFetch(&ref_count_, 1); } bool Release() { - if (--ref_count_ == 0) { + Atomic32 val = AtomicAddFetch(&ref_count_, -1); + if (val == 0) { Destroy(); delete this; return false; @@ -31,18 +58,18 @@ class RefObject { return true; } - protected: virtual ~RefObject() { pthread_mutex_destroy(&lock_); } // Override to clean up object when last reference is released. virtual void Destroy() {} - pthread_mutex_t lock_; private: - int ref_count_; + Atomic32 ref_count_; + + friend class ScopedRefBase; }; #endif // LIBRARIES_SDK_UTIL_REF_OBJECT diff --git a/native_client_sdk/src/libraries/sdk_util/scoped_ref.h b/native_client_sdk/src/libraries/sdk_util/scoped_ref.h new file mode 100644 index 0000000000..0bbba713d5 --- /dev/null +++ b/native_client_sdk/src/libraries/sdk_util/scoped_ref.h @@ -0,0 +1,91 @@ +/* Copyright 2013 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. + */ + +#ifndef LIBRARIES_SDK_UTIL_SCOPED_REF_H_ +#define LIBRARIES_SDK_UTIL_SCOPED_REF_H_ + +#include <stdlib.h> + +#include "sdk_util/macros.h" +#include "sdk_util/ref_object.h" + +class ScopedRefBase { + protected: + ScopedRefBase() : ptr_(NULL) {} + ~ScopedRefBase() { reset(NULL); } + + void reset(RefObject* obj) { + if (obj) { + obj->Acquire(); + } + if (ptr_) { + ptr_->Release(); + } + ptr_ = obj; + } + + protected: + RefObject* ptr_; +}; + +template <class T> +class ScopedRef : public ScopedRefBase { + public: + ScopedRef() {} + ScopedRef(const ScopedRef& ptr) { reset(ptr.get()); } + explicit ScopedRef(T* ptr) { reset(ptr); } + + ScopedRef& operator=(const ScopedRef& ptr) { + reset(ptr.get()); + return *this; + } + + template <typename U> + ScopedRef(const ScopedRef<U>& ptr) { + reset(ptr.get()); + } + + template <typename U> + ScopedRef& operator=(const ScopedRef<U>& ptr) { + reset(ptr.get()); + return *this; + } + + void reset(T* obj = NULL) { ScopedRefBase::reset(obj); } + T* get() const { return static_cast<T*>(ptr_); } + + template <typename U> + bool operator==(const ScopedRef<U>& p) const { + return get() == p.get(); + } + + template <typename U> + bool operator!=(const ScopedRef<U>& p) const { + return get() != p.get(); + } + + public: + T& operator*() const { return *get(); } + T* operator->() const { return get(); } + +#ifndef __llvm__ + private: + typedef void (ScopedRef::*bool_as_func_ptr)() const; + void bool_as_func_impl() const {}; + + public: + operator bool_as_func_ptr() const { + return (ptr_ != NULL) ? &ScopedRef::bool_as_func_impl : 0; + } +#else + /* + * TODO Remove when bug 3514 is fixed see: + * https://code.google.com/p/nativeclient/issues/detail?id=3514 + */ + operator T*() const { return get(); } +#endif +}; + +#endif // LIBRARIES_SDK_UTIL_SCOPED_REF_H_ diff --git a/native_client_sdk/src/libraries/xray/demangle.c b/native_client_sdk/src/libraries/xray/demangle.c new file mode 100644 index 0000000000..9871bd3ad8 --- /dev/null +++ b/native_client_sdk/src/libraries/xray/demangle.c @@ -0,0 +1,26 @@ +/* Copyright (c) 2013 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. + */ + +#include "xray/xray_priv.h" + +/* Note name demangling requires linking against libstdc++ */ +/* If your platform does not support __cxa_demangle, re-compile XRay with: */ +/* -DXRAY_NO_DEMANGLE */ + +#if !defined(XRAY_NO_DEMANGLE) +extern +char* __cxa_demangle(const char* __mangled_name, char* __output_buffer, + size_t* __length, int* __status); +#endif + +const char* XRayDemangle(char* demangle, size_t size, const char* symbol) { +#if !defined(XRAY_NO_DEMANGLE) + int stat; + __cxa_demangle(symbol, demangle, &size, &stat); + if (stat == 0) + return demangle; +#endif + return symbol; +} diff --git a/native_client_sdk/src/libraries/xray/hashtable.c b/native_client_sdk/src/libraries/xray/hashtable.c new file mode 100644 index 0000000000..9166cfe2e3 --- /dev/null +++ b/native_client_sdk/src/libraries/xray/hashtable.c @@ -0,0 +1,201 @@ +/* Copyright (c) 2013 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. + */ + + +/* Hashtable for XRay */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "xray/xray_priv.h" + +#if defined(XRAY) + +struct XRayHashTableEntry { + void* data; + uint32_t key; +}; + + +struct XRayHashTable { + int size; + int count; + struct XRayHashTableEntry* array; +}; + + +XRAY_NO_INSTRUMENT void XRayHashTableGrow(struct XRayHashTable* table); +XRAY_NO_INSTRUMENT uint32_t XRayHashTableHashKey(uint32_t key); +XRAY_NO_INSTRUMENT void XRayHashTableInit(struct XRayHashTable* table, + int32_t size); + +#define HASH_HISTO 1024 +int g_hash_histo[HASH_HISTO]; + + +/* Hashes a 32bit key into a 32bit value. */ +uint32_t XRayHashTableHashKey(uint32_t x) { + uint32_t y = x * 7919; + uint32_t z; + size_t c; + uint8_t* s = (uint8_t*)&y; + /* based on djb2 hash function */ + uint32_t h = 5381; + for (c = 0; c < sizeof(y); ++c) { + z = s[c]; + h = ((h << 5) + h) + z; + } + return h; +} + + +int XRayHashTableGetSize(struct XRayHashTable* table) { + return table->size; +} + + +/* Looks up key in hashtable and returns blind data. */ +void* XRayHashTableLookup(struct XRayHashTable* table, uint32_t key) { + uint32_t h = XRayHashTableHashKey(key); + uint32_t m = table->size - 1; + uint32_t j = h & m; + uint32_t i; + int z = 1; + for (i = 0; i < m; ++i) { + /* An empty entry means the {key, data} isn't in the table. */ + if (NULL == table->array[j].data) { + ++g_hash_histo[0]; + return NULL; + } + /* Search for address */ + if (table->array[j].key == key) { + if (z >= HASH_HISTO) + z = HASH_HISTO - 1; + ++g_hash_histo[z]; + return table->array[j].data; + } + j = (j + 1) & m; + ++z; + } + /* Table was full, and there wasn't a match. */ + return NULL; +} + + +/* Inserts key & data into hash table. No duplicates. */ +void* XRayHashTableInsert(struct XRayHashTable* table, + void* data, uint32_t key) { + uint32_t h = XRayHashTableHashKey(key); + uint32_t m = table->size - 1; + uint32_t j = h & m; + uint32_t i; + for (i = 0; i < m; ++i) { + /* Take the first empty entry. */ + /* (the key,data isn't already in the table) */ + if (NULL == table->array[j].data) { + void* ret; + float ratio; + table->array[j].data = data; + table->array[j].key = key; + ++table->count; + ret = data; + ratio = (float)table->count / (float)table->size; + /* Double the size of the symtable if we've hit the ratio. */ + if (ratio > XRAY_SYMBOL_TABLE_MAX_RATIO) + XRayHashTableGrow(table); + return ret; + } + /* If the key is already present, return the data in the table. */ + if (table->array[j].key == key) { + return table->array[j].data; + } + j = (j + 1) & m; + } + /* Table was full */ + return NULL; +} + + +void* XRayHashTableAtIndex(struct XRayHashTable* table, int i) { + if ((i < 0) || (i >= table->size)) + return NULL; + return table->array[i].data; +} + + +/* Grows the hash table by doubling its size, */ +/* then re-inserts all the elements into the new table. */ +void XRayHashTableGrow(struct XRayHashTable* table) { + struct XRayHashTableEntry* old_array = table->array; + int old_size = table->size; + int new_size = old_size * 2; + int i; + printf("XRay: Growing a hash table...\n"); + XRayHashTableInit(table, new_size); + for (i = 0; i < old_size; ++i) { + void* data = old_array[i].data; + if (NULL != data) { + uint32_t key = old_array[i].key; + XRayHashTableInsert(table, data, key); + } + } + XRayFree(old_array); +} + + +void XRayHashTableInit(struct XRayHashTable* table, int32_t size) { + size_t bytes; + if (0 != (size & (size - 1))) { + printf("Xray: Hash table size should be a power of 2!\n"); + /* Round size up to next power of 2 */ + /* see http://aggregate.org/MAGIC/ */ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + } + bytes = sizeof(table->array[0]) * size; + table->size = size; + table->count = 0; + table->array = (struct XRayHashTableEntry*)XRayMalloc(bytes); +} + + +/* Creates & inializes hash table. */ +struct XRayHashTable* XRayHashTableCreate(int size) { + struct XRayHashTable* table; + table = (struct XRayHashTable*)XRayMalloc(sizeof(*table)); + XRayHashTableInit(table, size); + memset(&g_hash_histo[0], 0, sizeof(g_hash_histo[0]) * HASH_HISTO); + return table; +} + + +/* Prints hash table performance to file; for debugging. */ +void XRayHashTableHisto(FILE* f) { + int i; + for (i = 0; i < HASH_HISTO; ++i) { + if (0 != g_hash_histo[i]) + fprintf(f, "hash_iterations[%d] = %d\n", i, g_hash_histo[i]); + } +} + + +/* Frees hash table. */ +/* Note: Does not free what the hash table entries point to. */ +void XRayHashTableFree(struct XRayHashTable* table) { + XRayFree(table->array); + table->size = 0; + table->count = 0; + table->array = NULL; + XRayFree(table); +} + +#endif /* XRAY */ + diff --git a/native_client_sdk/src/libraries/xray/library.dsc b/native_client_sdk/src/libraries/xray/library.dsc new file mode 100644 index 0000000000..26f6d8dbc1 --- /dev/null +++ b/native_client_sdk/src/libraries/xray/library.dsc @@ -0,0 +1,34 @@ +{ + 'TOOLS': ['newlib', 'glibc'], + 'SEARCH': [ + '.' + ], + 'TARGETS': [ + { + 'NAME' : 'xray', + 'TYPE' : 'lib', + 'SOURCES' : [ + 'demangle.c', + 'hashtable.c', + 'stringpool.c', + 'symtable.c', + 'xray.c' + ], + 'CCFLAGS': [ + '-DXRAY -DXRAY_ANNOTATE -O2' + ] + } + ], + 'HEADERS': [ + { + 'FILES': [ + 'xray.h', + 'xray_priv.h' + ], + 'DEST': 'include/xray', + } + ], + 'DEST': 'src', + 'NAME': 'xray', + 'EXPERIMENTAL': True, +} diff --git a/native_client_sdk/src/libraries/xray/stringpool.c b/native_client_sdk/src/libraries/xray/stringpool.c new file mode 100644 index 0000000000..4a63581a0c --- /dev/null +++ b/native_client_sdk/src/libraries/xray/stringpool.c @@ -0,0 +1,92 @@ +/* Copyright (c) 2013 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. + */ + + +/* XRay string pool */ + +/* String pool holds a large pile of strings. */ +/* It is up to higher level data structures to avoid duplicates. */ +/* It is up to higher level data structures to provide fast lookups. */ + +#include <stdlib.h> +#include <string.h> +#include "xray/xray_priv.h" + +#if defined(XRAY) + +struct XRayStringPoolNode { + struct XRayStringPoolNode* next; + char strings[XRAY_STRING_POOL_NODE_SIZE]; +}; + + +struct XRayStringPool { + struct XRayStringPoolNode* head; + struct XRayStringPoolNode* current; + int index; +}; + + +static struct XRayStringPoolNode* XRayStringPoolAllocNode() { + struct XRayStringPoolNode* s; + s = (struct XRayStringPoolNode *)XRayMalloc(sizeof(*s)); + s->next = NULL; + return s; +} + + +static int XRayStringPoolCurrentNodeSpaceAvail(struct XRayStringPool* pool) { + int i = pool->index; + return (XRAY_STRING_POOL_NODE_SIZE - i) - 1; +} + + +/* Append a string to the string pool. */ +char* XRayStringPoolAppend(struct XRayStringPool* pool, const char* src) { + /* Add +1 to STRING_POOL_NODE_SIZE to detect large strings */ + /* Add +1 to strnlen result to account for string termination */ + int n = strnlen(src, XRAY_STRING_POOL_NODE_SIZE + 1) + 1; + int a = XRayStringPoolCurrentNodeSpaceAvail(pool); + char* dst; + /* Don't accept strings larger than the pool node. */ + if (n >= (XRAY_STRING_POOL_NODE_SIZE - 1)) + return NULL; + /* If string doesn't fit, alloc a new node. */ + if (n > a) { + pool->current->next = XRayStringPoolAllocNode(); + pool->current = pool->current->next; + pool->index = 0; + } + /* Copy string and return a pointer to copy. */ + dst = &pool->current->strings[pool->index]; + strcpy(dst, src); + pool->index += n; + return dst; +} + + +/* Create & initialize a string pool instance. */ +struct XRayStringPool* XRayStringPoolCreate() { + struct XRayStringPool* pool; + pool = (struct XRayStringPool*)XRayMalloc(sizeof(*pool)); + pool->head = XRayStringPoolAllocNode(); + pool->current = pool->head; + return pool; +} + + +/* Free a string pool. */ +void XRayStringPoolFree(struct XRayStringPool* pool) { + struct XRayStringPoolNode* n = pool->head; + while (NULL != n) { + struct XRayStringPoolNode* c = n; + n = n->next; + XRayFree(c); + } + XRayFree(pool); +} + +#endif /* XRAY */ + diff --git a/native_client_sdk/src/libraries/xray/symtable.c b/native_client_sdk/src/libraries/xray/symtable.c new file mode 100644 index 0000000000..b285cb189a --- /dev/null +++ b/native_client_sdk/src/libraries/xray/symtable.c @@ -0,0 +1,264 @@ +/* Copyright (c) 2013 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. + */ + +/* XRay symbol table */ + +#define _GNU_SOURCE +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(__GLIBC__) +#include <dlfcn.h> +#endif + +#include "xray/xray_priv.h" + +#if defined(XRAY) + +bool g_symtable_debug = false; + +struct XRayFrameInfo { + int times_called; + int total_ticks; +}; + + +struct XRaySymbol { + const char* name; + struct XRayFrameInfo frames[XRAY_MAX_FRAMES]; +}; + + +struct XRaySymbolPoolNode { + struct XRaySymbolPoolNode* next; + struct XRaySymbol symbols[XRAY_SYMBOL_POOL_NODE_SIZE]; +}; + + +struct XRaySymbolPool { + struct XRaySymbolPoolNode* head; + struct XRaySymbolPoolNode* current; + int index; +}; + + +struct XRaySymbolTable { + int num_symbols; + struct XRayHashTable* hash_table; + struct XRayStringPool* string_pool; + struct XRaySymbolPool* symbol_pool; +}; + + +const char* XRaySymbolGetName(struct XRaySymbol* symbol) { + return (NULL == symbol) ? "(null)" : symbol->name; +} + + +struct XRaySymbol* XRaySymbolCreate(struct XRaySymbolPool* sympool, + const char* name) +{ + struct XRaySymbol* symbol; + symbol = XRaySymbolPoolAlloc(sympool); + symbol->name = name; + return symbol; +} + + +struct XRaySymbol* XRaySymbolPoolAlloc(struct XRaySymbolPool* sympool) { + struct XRaySymbol* symbol; + if (sympool->index >= XRAY_SYMBOL_POOL_NODE_SIZE) { + struct XRaySymbolPoolNode* new_pool; + new_pool = (struct XRaySymbolPoolNode*)XRayMalloc(sizeof(*new_pool)); + sympool->current->next = new_pool; + sympool->current = new_pool; + sympool->index = 0; + } + symbol = &sympool->current->symbols[sympool->index]; + ++sympool->index; + return symbol; +} + + +struct XRaySymbolPool* XRaySymbolPoolCreate() { + struct XRaySymbolPool* sympool; + struct XRaySymbolPoolNode* node; + sympool = (struct XRaySymbolPool*)XRayMalloc(sizeof(*sympool)); + node = (struct XRaySymbolPoolNode*)XRayMalloc(sizeof(*node)); + sympool->head = node; + sympool->current = node; + sympool->index = 0; + return sympool; +} + + +void XRaySymbolPoolFree(struct XRaySymbolPool* pool) { + struct XRaySymbolPoolNode* n = pool->head; + while (NULL != n) { + struct XRaySymbolPoolNode* c = n; + n = n->next; + XRayFree(c); + } + XRayFree(pool); +} + + +int XRaySymbolTableGetSize(struct XRaySymbolTable* symtab) { + return XRayHashTableGetSize(symtab->hash_table); +} + + +struct XRaySymbol* XRaySymbolTableAtIndex(struct XRaySymbolTable* symtab, + int i) { + return (struct XRaySymbol*)XRayHashTableAtIndex(symtab->hash_table, i); +} + +struct XRaySymbol* XRaySymbolTableAdd(struct XRaySymbolTable* symtab, + struct XRaySymbol* symbol, + uint32_t addr) { + return (struct XRaySymbol*) + XRayHashTableInsert(symtab->hash_table, symbol, addr); +} + +struct XRaySymbol* XRaySymbolTableAddByName(struct XRaySymbolTable* symtab, + const char* name, uint32_t addr) { + char* recorded_name; + struct XRaySymbol* symbol; + char buffer[XRAY_LINE_SIZE]; + const char* demangled_name = XRayDemangle(buffer, XRAY_LINE_SIZE, name); + /* record the demangled symbol name into the string pool */ + recorded_name = XRayStringPoolAppend(symtab->string_pool, demangled_name); + if (g_symtable_debug) + printf("adding symbol %s\n", recorded_name); + /* construct a symbol and put it in the symbol table */ + symbol = XRaySymbolCreate(symtab->symbol_pool, recorded_name); + return XRaySymbolTableAdd(symtab, symbol, addr); +} + +struct XRaySymbol* XRaySymbolTableLookup(struct XRaySymbolTable* symtab, + uint32_t addr) { + void *x = XRayHashTableLookup(symtab->hash_table, addr); + struct XRaySymbol* r = (struct XRaySymbol*)x; +#if defined(__GLIBC__) + if (r == NULL) { + Dl_info info; + if (dladdr((const void*)addr, &info) != 0) + if (info.dli_sname) + r = XRaySymbolTableAddByName(symtab, info.dli_sname, addr); + } +#endif + return r; +} + +struct XRaySymbol* XRaySymbolTableCreateEntry(struct XRaySymbolTable* symtab, + const char* line) { + uint32_t addr; + unsigned int uiaddr; + char symbol_text[XRAY_LINE_SIZE]; + char* parsed_symbol; + char* newln; + sscanf(line,"%x %s", &uiaddr, symbol_text); + if (uiaddr > 0x07FFFFFF) { + printf("While parsing the mapfile, XRay encountered:\n"); + printf("%s\n", line); + printf("XRay only works with code addresses 0x00000000 - 0x07FFFFFF\n"); + printf("All functions must reside in this address space.\n"); + exit(-1); + } + addr = (uint32_t)uiaddr; + parsed_symbol = strstr(line, symbol_text); + newln = strstr(parsed_symbol, "\n"); + if (NULL != newln) { + *newln = 0; + } + return XRaySymbolTableAddByName(symtab, parsed_symbol, addr); +} + + +void XRaySymbolTableParseMapfile(struct XRaySymbolTable* symtab, + const char* mapfile) +{ + FILE* f; + char line[XRAY_LINE_SIZE]; + bool in_text = false; + bool in_link_once = false; + int in_link_once_counter = 0; + int num_symbols = 0; + + printf("XRay: opening mapfile %s\n", mapfile); + f = fopen(mapfile, "rt"); + if (0 != f) { + printf("XRay: parsing...\n"); + while(NULL != fgets(line, XRAY_LINE_SIZE, f)) { + if (line == strstr(line, " .text ")) { + in_text = true; + continue; + } + if (line == strstr(line, " .gnu.linkonce.t.")) { + in_link_once = true; + in_link_once_counter = 0; + continue; + } + if (line == strstr(line, " .text.")) { + in_link_once = true; + in_link_once_counter = 0; + continue; + } + if (line == strstr(line, " 0x")) { + if (in_text) { + XRaySymbolTableCreateEntry(symtab, line); + ++num_symbols; + } else if (in_link_once) { + if (in_link_once_counter != 0) { + XRaySymbolTableCreateEntry(symtab, line); + ++num_symbols; + } else { + ++in_link_once_counter; + } + } + } else { + in_text = false; + in_link_once = false; + } + } + fclose(f); + printf("XRay: loaded %d symbols into symbol table\n", num_symbols); + } else { + printf("XRay: failed to open %s\n", mapfile); + } + symtab->num_symbols += num_symbols; +} + + +/* Returns total number of symbols in the table. */ +int XRaySymbolCount(struct XRaySymbolTable* symtab) { + return symtab->num_symbols; +} + + +/* Creates and inializes a symbol table. */ +struct XRaySymbolTable* XRaySymbolTableCreate(int size) { + struct XRaySymbolTable* symtab; + symtab = (struct XRaySymbolTable*)XRayMalloc(sizeof(*symtab)); + symtab->num_symbols = 0; + symtab->string_pool = XRayStringPoolCreate(); + symtab->hash_table = XRayHashTableCreate(size); + symtab->symbol_pool = XRaySymbolPoolCreate(); + return symtab; +} + + +/* Frees a symbol table. */ +void XRaySymbolTableFree(struct XRaySymbolTable* symtab) { + XRayStringPoolFree(symtab->string_pool); + XRaySymbolPoolFree(symtab->symbol_pool); + XRayHashTableFree(symtab->hash_table); + symtab->num_symbols = 0; + XRayFree(symtab); +} + +#endif /* XRAY */ diff --git a/native_client_sdk/src/libraries/xray/xray.c b/native_client_sdk/src/libraries/xray/xray.c new file mode 100644 index 0000000000..6a3f3ae689 --- /dev/null +++ b/native_client_sdk/src/libraries/xray/xray.c @@ -0,0 +1,825 @@ +/* Copyright (c) 2013 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. + */ + + +/* XRay -- a simple profiler for Native Client */ + +#include <alloca.h> +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "xray/xray_priv.h" + +#if defined(XRAY) + +#define FORCE_INLINE __attribute__((always_inline)) + +#if defined(__amd64__) +FORCE_INLINE uint64_t RDTSC64() { + uint64_t a, d; + __asm__ __volatile__("rdtsc" : "=a" (a), "=d" (d)); + return ((uint64_t)a) | (((uint64_t)d) << 32); +} +#define RDTSC(_x) _x = RDTSC64() +#else +#define RDTSC(_x) __asm__ __volatile__ ("rdtsc" : "=A" (_x)); +#endif + + +/* Use a TLS variable for cheap thread uid. */ +volatile __thread int g_xray_thread_id; + + +struct XRayTraceStackEntry { + uint32_t depth_addr; + uint64_t tsc; + uint32_t dest; + uint32_t annotation_index; +}; + + +struct XRayTraceBufferEntry { + uint32_t depth_addr; + uint32_t annotation_index; + uint64_t ticks; +}; + + +struct XRayTraceFrameEntry { + /* Indices into global tracebuffer */ + int start; + int end; + uint64_t start_tsc; + uint64_t end_tsc; + uint64_t total_ticks; + int annotation_count; + bool valid; +}; + + +struct XRayTraceFrame { + struct XRayTraceFrameEntry* entry; + int head; + int tail; + int count; +}; + + +struct XRayTotal { + int index; + int frame; + uint64_t ticks; +}; + + +struct XRayTraceCapture { + /* Common variables share cache line */ + volatile void* recording; + uint32_t stack_depth; + uint32_t max_stack_depth; + int buffer_index; + int buffer_size; + int disabled; + int annotation_count; + struct XRaySymbolTable* symbols; + bool initialized; + uint32_t annotation_filter; + uint32_t guard0; + struct XRayTraceStackEntry stack[XRAY_TRACE_STACK_SIZE] XRAY_ALIGN64; + uint32_t guard1; + char annotation[XRAY_ANNOTATION_STACK_SIZE] XRAY_ALIGN64; + uint32_t guard2; + struct XRayTraceBufferEntry* buffer; + struct XRayTraceFrame frame; +} XRAY_ALIGN64; + + +#ifdef __cplusplus +extern "C" { +#endif + +XRAY_NO_INSTRUMENT void __cyg_profile_func_enter(void* this_fn, + void* call_site); +XRAY_NO_INSTRUMENT void __cyg_profile_func_exit(void* this_fn, + void* call_site); +XRAY_NO_INSTRUMENT void __xray_profile_append_annotation( + struct XRayTraceStackEntry* se, struct XRayTraceBufferEntry* be); +XRAY_NO_INSTRUMENT int XRayTraceBufferGetTraceCountForFrame(int frame); +XRAY_NO_INSTRUMENT int XRayTraceBufferIncrementIndex(int i); +XRAY_NO_INSTRUMENT int XRayTraceBufferDecrementIndex(int i); +XRAY_NO_INSTRUMENT bool XRayTraceBufferIsAnnotation(int index); +XRAY_NO_INSTRUMENT void XRayTraceBufferAppendString(char* src); +XRAY_NO_INSTRUMENT int XRayTraceBufferCopyToString(int index, char* dst); +XRAY_NO_INSTRUMENT int XRayTraceBufferSkipAnnotation(int index); +XRAY_NO_INSTRUMENT int XRayTraceBufferNextEntry(int index); +XRAY_NO_INSTRUMENT void XRayCheckGuards(); +XRAY_NO_INSTRUMENT void XRayFrameMakeLabel(int counter, char* label); +XRAY_NO_INSTRUMENT int XRayFrameFindTail(); + +#ifdef __cplusplus +} +#endif + + +struct XRayTraceCapture g_xray = { + NULL, 0, 0, 0, 0, 0, 0, NULL, false, 0xFFFFFFFF +}; + + +/* Increments the trace index, wrapping around if needed. */ +XRAY_FORCE_INLINE int XRayTraceBufferIncrementIndex(int index) { + ++index; + if (index >= g_xray.buffer_size) + index = 0; + return index; +} + + +/* Decrements the trace index, wrapping around if needed. */ +XRAY_FORCE_INLINE int XRayTraceBufferDecrementIndex(int index) { + --index; + if (index < 0) + index = g_xray.buffer_size - 1; + return index; +} + + +/* Returns true if the trace entry is an annotation string. */ +FORCE_INLINE bool XRayTraceBufferIsAnnotation(int index) { + struct XRayTraceBufferEntry* be = &g_xray.buffer[index]; + char* dst = (char*)be; + return 0 == *dst; +} + + +/* Asserts that the guard values haven't changed. */ +void XRayCheckGuards() { + assert(g_xray.guard0 == XRAY_GUARD_VALUE); + assert(g_xray.guard1 == XRAY_GUARD_VALUE); + assert(g_xray.guard2 == XRAY_GUARD_VALUE); +} + + +/* Not very accurate, as the annotation strings will also +** be counted as "entries" */ +int XRayTraceBufferGetTraceCountForFrame(int frame) { + assert(true == g_xray.initialized); + assert(frame >= 0); + assert(frame < g_xray.frame.count); + assert(NULL == g_xray.recording); + int start = g_xray.frame.entry[frame].start; + int end = g_xray.frame.entry[frame].end; + int num; + if (start < end) + num = end - start; + else + num = g_xray.buffer_size - (start - end); + return num; +} + + +/* Generic memory malloc for XRay */ +/* validates pointer returned by malloc */ +/* memsets memory block to zero */ +void* XRayMalloc(size_t t) { + void* data; + data = malloc(t); + if (NULL == data) { + printf("XRay: malloc(%d) failed, panic shutdown!\n", t); + exit(-1); + } + memset(data, 0, t); + return data; +} + + +/* Generic memory free for XRay */ +void XRayFree(void* data) { + assert(NULL != data); + free(data); +} + + +/* Append a string to trace buffer. */ +void XRayTraceBufferAppendString(char* src) { + int index = g_xray.buffer_index; + bool done = false; + int start_index = 1; + int s = 0; + int i; + char* dst = (char*)&g_xray.buffer[index]; + const int num = sizeof(g_xray.buffer[index]); + dst[0] = 0; + while (!done) { + for (i = start_index; i < num; ++i) { + dst[i] = src[s]; + if (0 == src[s]) { + dst[i] = 0; + done = true; + break; + } + ++s; + } + index = XRayTraceBufferIncrementIndex(index); + dst = (char*)&g_xray.buffer[index]; + start_index = 0; + } + g_xray.buffer_index = index; +} + + +/* Copies annotation from trace buffer to output string. */ +int XRayTraceBufferCopyToString(int index, char* dst) { + assert(XRayTraceBufferIsAnnotation(index)); + bool done = false; + int i; + int d = 0; + int start_index = 1; + while (!done) { + char* src = (char*) &g_xray.buffer[index]; + const int num = sizeof(g_xray.buffer[index]); + for (i = start_index; i < num; ++i) { + dst[d] = src[i]; + if (0 == src[i]) { + done = true; + break; + } + ++d; + } + index = XRayTraceBufferIncrementIndex(index); + start_index = 0; + } + return index; +} + + +/* Main profile capture function that is called at the start */ +/* of every instrumented function. This function is implicitly */ +/* called when code is compilied with the -finstrument-functions option */ +void __cyg_profile_func_enter(void* this_fn, void* call_site) { + if (&g_xray_thread_id == g_xray.recording) { + uint32_t depth = g_xray.stack_depth; + if (depth < g_xray.max_stack_depth) { + struct XRayTraceStackEntry* se = &g_xray.stack[depth]; + uint32_t addr = (uint32_t)this_fn; + se->depth_addr = XRAY_PACK_DEPTH_ADDR(depth, addr); + se->dest = g_xray.buffer_index; + se->annotation_index = 0; + RDTSC(se->tsc); + g_xray.buffer_index = + XRayTraceBufferIncrementIndex(g_xray.buffer_index); + } + ++g_xray.stack_depth; + } +} + + +/* Main profile capture function that is called at the exit of */ +/* every instrumented function. This function is implicity called */ +/* when the code is compiled with the -finstrument-functions option */ +void __cyg_profile_func_exit(void* this_fn, void* call_site) { + if (&g_xray_thread_id == g_xray.recording) { + --g_xray.stack_depth; + if (g_xray.stack_depth < g_xray.max_stack_depth) { + uint32_t depth = g_xray.stack_depth; + struct XRayTraceStackEntry* se = &g_xray.stack[depth]; + uint32_t buffer_index = se->dest; + uint64_t tsc; + struct XRayTraceBufferEntry* be = &g_xray.buffer[buffer_index]; + RDTSC(tsc); + be->depth_addr = se->depth_addr; + be->ticks = tsc - se->tsc; + be->annotation_index = 0; + if (0 != se->annotation_index) + __xray_profile_append_annotation(se, be); + } + } +} + + +/* Special case appending annotation string to trace buffer */ +/* this function should only ever be called from __cyg_profile_func_exit() */ +void __xray_profile_append_annotation(struct XRayTraceStackEntry* se, + struct XRayTraceBufferEntry* be) { + struct XRayTraceStackEntry* parent = se - 1; + int start = parent->annotation_index; + be->annotation_index = g_xray.buffer_index; + char* str = &g_xray.annotation[start]; + XRayTraceBufferAppendString(str); + *str = 0; + ++g_xray.annotation_count; +} + + + +/* Annotates the trace buffer. no filtering. */ +void __XRayAnnotate(const char* fmt, ...) { + va_list args; + /* Only annotate functions recorded in the trace buffer. */ + if (true == g_xray.initialized) { + if (0 == g_xray.disabled) { + if (&g_xray_thread_id == g_xray.recording) { + char buffer[1024]; + int r; + va_start(args, fmt); + r = vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + { + /* Get current string ptr */ + int depth = g_xray.stack_depth - 1; + struct XRayTraceStackEntry* se = &g_xray.stack[depth]; + if (0 == se->annotation_index) { + struct XRayTraceStackEntry* parent = se - 1; + se->annotation_index = parent->annotation_index; + } + char* dst = &g_xray.annotation[se->annotation_index]; + strcpy(dst, buffer); + int len = strlen(dst); + se->annotation_index += len; + } + } + } + } +} + + +/* Annotates the trace buffer with user strings. Can be filtered. */ +void __XRayAnnotateFiltered(const uint32_t filter, const char* fmt, ...) { + va_list args; + if (true == g_xray.initialized) { + if (0 != (filter & g_xray.annotation_filter)) { + if (0 == g_xray.disabled) { + if (&g_xray_thread_id == g_xray.recording) { + char buffer[1024]; + int r; + va_start(args, fmt); + r = vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + { + /* get current string ptr */ + int depth = g_xray.stack_depth - 1; + struct XRayTraceStackEntry* se = &g_xray.stack[depth]; + if (0 == se->annotation_index) { + struct XRayTraceStackEntry* parent = se - 1; + se->annotation_index = parent->annotation_index; + } + char* dst = &g_xray.annotation[se->annotation_index]; + strcpy(dst, buffer); + int len = strlen(dst); + se->annotation_index += len; + } + } + } + } + } +} + + +/* Allows user to specify annotation filter value, a 32 bit mask. */ +void XRaySetAnnotationFilter(uint32_t filter) { + g_xray.annotation_filter = filter; +} + + +/* Reset xray profiler. */ +void XRayReset() { + assert(true == g_xray.initialized); + assert(NULL == g_xray.recording); + g_xray.buffer_index = 0; + g_xray.stack_depth = 0; + g_xray.disabled = 0; + g_xray.frame.head = 0; + g_xray.frame.tail = 0; + memset(g_xray.frame.entry, 0, + sizeof(g_xray.frame.entry[0]) * g_xray.frame.count); + memset(&g_xray.stack, 0, + sizeof(g_xray.stack[0]) * XRAY_TRACE_STACK_SIZE); + XRayCheckGuards(); +} + + +/* Change the maximum stack depth captures are made. */ +void XRaySetMaxStackDepth(int stack_depth) { + assert(true == g_xray.initialized); + assert(NULL == g_xray.recording); + if (stack_depth < 1) + stack_depth = 1; + if (stack_depth >= XRAY_TRACE_STACK_SIZE) + stack_depth = (XRAY_TRACE_STACK_SIZE - 1); + g_xray.max_stack_depth = stack_depth; +} + + +int XRayFramePrev(int i) { + i = i - 1; + if (i < 0) + i = g_xray.frame.count - 1; + return i; +} + + +int XRayFrameNext(int i) { + i = i + 1; + if (i >= g_xray.frame.count) + i = 0; + return i; +} + + +void XRayFrameMakeLabel(int counter, char* label) { + snprintf(label, XRAY_MAX_LABEL, "@@@frame%d", counter); +} + + +/* Scans the ring buffer going backwards to find last valid complete frame. */ +int XRayFrameFindTail() { + int head = g_xray.frame.head; + int index = XRayFramePrev(head); + int total_capture = 0; + int last_valid_frame = index; + /* Check for no captures */ + if (g_xray.frame.head == g_xray.frame.tail) + return g_xray.frame.head; + /* Go back and invalidate all captures that have been stomped. */ + while (index != head) { + bool valid = g_xray.frame.entry[index].valid; + if (valid) { + total_capture += XRayTraceBufferGetTraceCountForFrame(index) + 1; + if (total_capture < g_xray.buffer_size) { + last_valid_frame = index; + g_xray.frame.entry[index].valid = true; + } else { + g_xray.frame.entry[index].valid = false; + } + } + index = XRayFramePrev(index); + } + return last_valid_frame; +} + + +/* Starts a new frame and enables capturing, */ +/* must be paired with XRayEndFrame() */ +void XRayStartFrame() { + int i = g_xray.frame.head; + assert(true == g_xray.initialized); + assert(NULL == g_xray.recording); + XRayCheckGuards(); + /* Add a trace entry marker so we can detect wrap around stomping */ + struct XRayTraceBufferEntry* be = &g_xray.buffer[g_xray.buffer_index]; + be->depth_addr = XRAY_FRAME_MARKER; + g_xray.buffer_index = XRayTraceBufferIncrementIndex(g_xray.buffer_index); + /* Set start of the frame we're about to trace */ + g_xray.frame.entry[i].start = g_xray.buffer_index; + g_xray.disabled = 0; + g_xray.stack_depth = 1; + /* The trace stack[0] is reserved */ + memset(&g_xray.stack[0], 0, sizeof(g_xray.stack[0])); + /* Annotation index 0 is reserved to indicate no annotation */ + g_xray.stack[0].annotation_index = 1; + g_xray.annotation[0] = 0; + g_xray.annotation[1] = 0; + g_xray.annotation_count = 0; + g_xray.recording = &g_xray_thread_id; + RDTSC(g_xray.frame.entry[i].start_tsc); +} + + +/* Ends a frame and disables capturing. */ +/* Must be paired with XRayStartFrame() */ +/* Advances to the next frame. */ +void XRayEndFrame() { + int i = g_xray.frame.head; + assert(true == g_xray.initialized); + assert(NULL != g_xray.recording); + assert(0 == g_xray.disabled); + assert(1 == g_xray.stack_depth); + RDTSC(g_xray.frame.entry[i].end_tsc); + g_xray.frame.entry[i].total_ticks = + g_xray.frame.entry[i].end_tsc - g_xray.frame.entry[i].start_tsc; + g_xray.recording = NULL; + g_xray.frame.entry[i].end = g_xray.buffer_index; + g_xray.frame.entry[i].valid = true; + g_xray.frame.entry[i].annotation_count = g_xray.annotation_count; + g_xray.frame.head = XRayFrameNext(g_xray.frame.head); + /* If the table is filled, bump the tail. */ + if (g_xray.frame.head == g_xray.frame.tail) + g_xray.frame.tail = XRayFrameNext(g_xray.frame.tail); + g_xray.frame.tail = XRayFrameFindTail(); + /* Check that we didn't stomp over trace entry marker. */ + int marker = XRayTraceBufferDecrementIndex(g_xray.frame.entry[i].start); + struct XRayTraceBufferEntry* be = &g_xray.buffer[marker]; + if (be->depth_addr != XRAY_FRAME_MARKER) { + fprintf(stderr, + "XRay: XRayStopFrame() detects insufficient trace buffer size!\n"); + XRayReset(); + } else { + /* Replace marker with an empty annotation string. */ + be->depth_addr = XRAY_NULL_ANNOTATION; + XRayCheckGuards(); + } +} + + +/* Get the last frame captured. Do not call while capturing. */ +/* (ie call outside of XRayStartFrame() / XRayStopFrame() pair) */ +int XRayGetLastFrame() { + assert(true == g_xray.initialized); + assert(NULL == g_xray.recording); + assert(0 == g_xray.disabled); + assert(1 == g_xray.stack_depth); + int last_frame = XRayFramePrev(g_xray.frame.head); + return last_frame; +} + + +/* Disables capturing until a paired XRayEnableCapture() is called */ +/* This call can be nested, but must be paired with an enable */ +/* (If you need to just exclude a specific function and not its */ +/* children, the XRAY_NO_INSTRUMENT modifier might be better) */ +void XRayDisableCapture() { + assert(true == g_xray.initialized); + assert(NULL != g_xray.recording); + ++g_xray.disabled; + g_xray.recording = NULL; +} + + +/* Re-enables capture. Must be paired with XRayDisableCapture() */ +void XRayEnableCapture() { + assert(true == g_xray.initialized); + assert(NULL == g_xray.recording); + assert(0 < g_xray.disabled); + --g_xray.disabled; + if (0 == g_xray.disabled) { + g_xray.recording = &g_xray_thread_id; + } +} + + +/* The entry in the tracebuffer at index is an annotation string. */ +/* Calculate the next index value representing the next trace entry. */ +int XRayTraceBufferSkipAnnotation(int index) { + /* Annotations are strings embedded in the trace buffer. */ + /* An annotation string can span multiple trace entries. */ + /* Skip over the string by looking for zero termination. */ + assert(XRayTraceBufferIsAnnotation(index)); + bool done = false; + int start_index = 1; + int i; + while (!done) { + char* str = (char*) &g_xray.buffer[index]; + const int num = sizeof(g_xray.buffer[index]); + for (i = start_index; i < num; ++i) { + if (0 == str[i]) { + done = true; + break; + } + } + index = XRayTraceBufferIncrementIndex(index); + start_index = 0; + } + return index; +} + + +/* Starting at index, return the index into the trace buffer */ +/* for the next trace entry. Index can wrap (ringbuffer) */ +int XRayTraceBufferNextEntry(int index) { + if (XRayTraceBufferIsAnnotation(index)) + index = XRayTraceBufferSkipAnnotation(index); + else + index = XRayTraceBufferIncrementIndex(index); + return index; +} + + +/* Dumps the trace report for a given frame. */ +void XRayTraceReport(FILE* f, int frame, char* label, + float percent_cutoff, int ticks_cutoff) { + int index; + int start; + int end; + float total; + char space[257]; + memset(space, ' ', 256); + space[256] = 0; + if (NULL == f) { + f = stdout; + } + fprintf(f, + "======================================================================\n"); + if (NULL != label) + fprintf(f, "label %s\n", label); + fprintf(f, "\n"); + fprintf(f, + " Address Ticks Percent Function <optional annotation>\n"); + fprintf(f, + "----------------------------------------------------------------------\n"); + start = g_xray.frame.entry[frame].start; + end = g_xray.frame.entry[frame].end; + total = (float)g_xray.frame.entry[frame].total_ticks; + index = start; + while (index != end) { + if (!XRayTraceBufferIsAnnotation(index)) { + const char* symbol_name; + char annotation[1024]; + struct XRayTraceBufferEntry* e = &g_xray.buffer[index]; + uint32_t depth = XRAY_EXTRACT_DEPTH(e->depth_addr); + uint32_t addr = XRAY_EXTRACT_ADDR(e->depth_addr); + uint32_t ticks = (e->ticks); + uint32_t annotation_index = (e->annotation_index); + float percent = 100.0f * (float)ticks / total; + if (percent >= percent_cutoff && ticks >= ticks_cutoff) { + struct XRaySymbol* symbol; + symbol = XRaySymbolTableLookup(g_xray.symbols, addr); + symbol_name = XRaySymbolGetName(symbol); + if (0 != annotation_index) { + XRayTraceBufferCopyToString(annotation_index, annotation); + } else { + strcpy(annotation, ""); + } + fprintf(f, "0x%08X %10ld %5.1f %s%s %s\n", + (unsigned int)addr, (int64_t)ticks, percent, + &space[256 - depth], symbol_name, annotation); + } + } + index = XRayTraceBufferNextEntry(index); + } +} + + +int qcompare(const void* a, const void* b) { + struct XRayTotal* ia = (struct XRayTotal*)a; + struct XRayTotal* ib = (struct XRayTotal*)b; + return ib->ticks - ia->ticks; +} + + +/* Dumps a frame report */ +void XRayFrameReport(FILE* f) { + int i; + int head; + int frame; + int counter = 0; + int total_capture = 0; + struct XRayTotal* totals; + totals = (struct XRayTotal*) + alloca(g_xray.frame.count * sizeof(struct XRayTotal)); + frame = g_xray.frame.tail; + head = g_xray.frame.head; + fprintf(f, "\n"); + fprintf(f, + "Frame# Total Ticks Capture size Annotations Label\n"); + fprintf(f, + "----------------------------------------------------------------------\n"); + while (frame != head) { + int64_t total_ticks = g_xray.frame.entry[frame].total_ticks; + int capture_size = XRayTraceBufferGetTraceCountForFrame(frame); + int annotation_count = g_xray.frame.entry[frame].annotation_count; + bool valid = g_xray.frame.entry[frame].valid; + char label[XRAY_MAX_LABEL]; + XRayFrameMakeLabel(counter, label); + fprintf(f, " %3d %s %10ld %10d %10d %s\n", + counter, + valid ? " " : "*", + (int64_t)total_ticks, + capture_size, + annotation_count, + label); + totals[counter].index = counter; + totals[counter].frame = frame; + totals[counter].ticks = total_ticks; + total_capture += capture_size; + frame = XRayFrameNext(frame); + ++counter; + } + fprintf(f, + "----------------------------------------------------------------------\n"); + fprintf(f, + "XRay: %d frame(s) %d total capture(s)\n", counter, total_capture); + fprintf(f, "\n"); + /* Sort and take average of the median cut */ + qsort(totals, counter, sizeof(struct XRayTotal), qcompare); + fprintf(f, "\n"); + fprintf(f, "Sorted by total ticks (most expensive first):\n"); + fprintf(f, "\n"); + fprintf(f, + "Frame# Total Ticks Capture size Annotations Label\n"); + fprintf(f, + "----------------------------------------------------------------------\n"); + for (i = 0; i < counter; ++i) { + int index = totals[i].index; + int frame = totals[i].frame; + int64_t total_ticks = g_xray.frame.entry[frame].total_ticks; + int capture_size = XRayTraceBufferGetTraceCountForFrame(frame); + int annotation_count = g_xray.frame.entry[frame].annotation_count; + char label[XRAY_MAX_LABEL]; + XRayFrameMakeLabel(index, label); + fprintf(f, " %3d %10ld %10d %10d %s\n", + index, + (int64_t)total_ticks, + capture_size, + annotation_count, + label); + } +} + + +/* Dump a frame report followed by trace report(s) for each frame. */ +void XRayReport(FILE* f, float percent_cutoff, int ticks_cutoff) { + int head; + int index; + int counter = 0; + if (g_xray.symbols) + fprintf(f, "Number of symbols: %d\n", XRaySymbolCount(g_xray.symbols)); + XRayFrameReport(f); + fprintf(f, "\n"); + head = g_xray.frame.head; + index = g_xray.frame.tail; + while (index != head) { + char label[XRAY_MAX_LABEL]; + fprintf(f, "\n"); + XRayFrameMakeLabel(counter, label); + XRayTraceReport(f, index, label, percent_cutoff, ticks_cutoff); + index = XRayFrameNext(index); + + ++counter; + } + fprintf(f, + "======================================================================\n"); +#if defined(XRAY_OUTPUT_HASH_COLLISIONS) + XRayHashTableHisto(f); +#endif + fflush(f); +} + + +/* Write a profile report to text file. */ +void XRaySaveReport(const char* filename, + float percent_cutoff, + int ticks_cutoff) { + FILE* f; + f = fopen(filename, "wt"); + if (NULL != f) { + XRayReport(f, percent_cutoff, ticks_cutoff); + fclose(f); + } +} + + +/* Initialize XRay */ +void XRayInit(int stack_depth, int buffer_size, int frame_count, + const char* mapfilename) { + int adj_frame_count = frame_count + 1; + assert(false == g_xray.initialized); + size_t buffer_size_in_bytes = + sizeof(g_xray.buffer[0]) * buffer_size; + size_t frame_size_in_bytes = + sizeof(g_xray.frame.entry[0]) * adj_frame_count; + g_xray.buffer = + (struct XRayTraceBufferEntry *)XRayMalloc(buffer_size_in_bytes); + g_xray.frame.entry = + (struct XRayTraceFrameEntry *)XRayMalloc(frame_size_in_bytes); + + g_xray.buffer_size = buffer_size; + g_xray.frame.count = adj_frame_count; + g_xray.frame.head = 0; + g_xray.frame.tail = 0; + g_xray.disabled = 0; + g_xray.annotation_filter = 0xFFFFFFFF; + g_xray.guard0 = XRAY_GUARD_VALUE; + g_xray.guard1 = XRAY_GUARD_VALUE; + g_xray.guard2 = XRAY_GUARD_VALUE; + g_xray.initialized = true; + XRaySetMaxStackDepth(stack_depth); + XRayReset(); + + /* Mapfile is optional; we don't need it for captures, only for reports. */ + g_xray.symbols = XRaySymbolTableCreate(XRAY_DEFAULT_SYMBOL_TABLE_SIZE); + if (NULL != mapfilename) + XRaySymbolTableParseMapfile(g_xray.symbols, mapfilename); +} + + +/* Shut down and free memory used by XRay. */ +void XRayShutdown() { + assert(true == g_xray.initialized); + assert(NULL == g_xray.recording); + XRayCheckGuards(); + if (NULL != g_xray.symbols) { + XRaySymbolTableFree(g_xray.symbols); + } + XRayFree(g_xray.frame.entry); + XRayFree(g_xray.buffer); + g_xray.initialized = false; +} + +#endif /* XRAY */ diff --git a/native_client_sdk/src/libraries/xray/xray.h b/native_client_sdk/src/libraries/xray/xray.h new file mode 100644 index 0000000000..9039fdcc79 --- /dev/null +++ b/native_client_sdk/src/libraries/xray/xray.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2013 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. + */ + +/* XRay -- a simple profiler for Native Client */ + + +#ifndef LIBRARIES_XRAY_XRAY_H_ +#define LIBRARIES_XRAY_XRAY_H_ + +#include <stdint.h> + +#if defined(__arm__) +#undef XRAY +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define XRAY_NO_INSTRUMENT __attribute__((no_instrument_function)) +#define XRAY_INLINE __attribute__((always_inline, no_instrument_function)) + +#if defined(XRAY) + +/* Do not call __XRayAnnotate* directly; instead use the */ +/* XRayAnnotate() macros below. */ +XRAY_NO_INSTRUMENT void __XRayAnnotate(const char* str, ...) + __attribute__ ((format(printf, 1, 2))); +XRAY_NO_INSTRUMENT void __XRayAnnotateFiltered(const uint32_t filter, + const char* str, ...) __attribute__ ((format(printf, 2, 3))); + +/* This is the beginning of the public XRay API */ + +/* Ok if mapfilename is NULL, no symbols will be loaded. On glibc builds, + * XRay will also attempt to populate the symbol table with dladdr() + */ +XRAY_NO_INSTRUMENT void XRayInit(int stack_size, int buffer_size, + int frame_count, const char* mapfilename); +XRAY_NO_INSTRUMENT void XRayShutdown(); +XRAY_NO_INSTRUMENT void XRayStartFrame(); +XRAY_NO_INSTRUMENT void XRayEndFrame(); +XRAY_NO_INSTRUMENT void XRaySetAnnotationFilter(uint32_t filter); +XRAY_NO_INSTRUMENT void XRaySaveReport(const char* filename, + float percent_cutoff, + int cycle_cutoff); +XRAY_NO_INSTRUMENT void XRayReport(FILE* f, + float percent_cutoff, + int ticks_cutoff); +#if defined(XRAY_ANNOTATE) +#define XRayAnnotate(...) __XRayAnnotate(__VA_ARGS__) +#define XRayAnnotateFiltered(...) __XRayAnnotateFiltered(__VA_ARGS__) +#else +#define XRayAnnotate(...) +#define XRayAnnotateFiltered(...) +#endif +/* This is the end of the public XRay API */ + +#else /* defined(XRAY) */ + +/* Builds that don't define XRAY will use these 'null' functions instead. */ + +#define XRayAnnotate(...) +#define XRayAnnotateFiltered(...) + +inline void XRayInit(int stack_size, int buffer_size, + int frame_count, const char* mapfilename) {} +inline void XRayShutdown() {} +inline void XRayStartFrame() {} +inline void XRayEndFrame() {} +inline void XRaySetAnnotationFilter(uint32_t filter) {} +inline void XRaySaveReport(const char* filename, + float percent_cutoff, + int cycle_cutoff) {} +inline void XRayReport(FILE* f, float percent_cutoff, int ticks_cutoff); +#endif /* defined(XRAY) */ + +#ifdef __cplusplus +} +#endif + +#endif /* LIBRARIES_XRAY_XRAY_H_ */ + diff --git a/native_client_sdk/src/libraries/xray/xray.html b/native_client_sdk/src/libraries/xray/xray.html new file mode 100644 index 0000000000..6c249a2d1b --- /dev/null +++ b/native_client_sdk/src/libraries/xray/xray.html @@ -0,0 +1,85 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<HTML> +<HEAD> + <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8"> + <TITLE></TITLE> + <META NAME="GENERATOR" CONTENT="LibreOffice 3.5 (Linux)"> + <META NAME="AUTHOR" CONTENT="Nicholas Fullagar"> + <META NAME="CREATED" CONTENT="20130521;14423200"> + <META NAME="CHANGEDBY" CONTENT="Nicholas Fullagar"> + <META NAME="CHANGED" CONTENT="20130619;14414300"> + <STYLE TYPE="text/css"> + <!-- + @page { margin: 0.79in } + P { margin-bottom: 0.08in } + --> + </STYLE> +</HEAD> +<BODY LANG="en-US" DIR="LTR"> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3><SPAN STYLE="font-weight: normal">Guests, +introduction.</SPAN></FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><BR> +</P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3><SPAN STYLE="font-weight: normal">Brief +description of other PACC meetings.</SPAN></FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><BR> +</P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3><SPAN STYLE="font-weight: normal">Dan +Hughes</SPAN></FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><BR> +</P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3><SPAN STYLE="font-weight: normal">Dan +attended Rochester Institute of Technology, studying advertising +photography and specializing in digital engineering. After +graduating, Dan worked as a commercial shooter, digital retoucher and + for a stint ran a fine art photography gallery, making and framing +fine art prints. For the past three years, Dan has been organizing, +creating and delivering photography education for Nik Software and +now Google.</SPAN></FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><BR> +</P> +<P STYLE="margin-bottom: 0in"><BR> +</P> +<P STYLE="margin-bottom: 0in"><BR> +</P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Nik +Module Votes (*)
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>------------------------------ +--------------------
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Sharpener: + XXXXXoooo:..
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Silver +Efex: XXXXoo:..
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Dfine: + XXXo:::....
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Viveza: + Xoo:...
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Color +Efex: o::...
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>HDR +Efex: X:....
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>(*) +key: X first o second : third . fourth+all
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Software + Votes (**) Total
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>------------------------------ +--------------------
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Lightroom: + XXXXXoooo 9
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Photoshop: + XXXooooe 8
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Not +stated: 3
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>(**) +key: X use primary o use both e Photoshop Elements
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>
</FONT></FONT></P> +<P STYLE="margin-bottom: 0in"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Total +replies: 16
</FONT></FONT></P> +</BODY> +</HTML>
\ No newline at end of file diff --git a/native_client_sdk/src/libraries/xray/xray.odt b/native_client_sdk/src/libraries/xray/xray.odt Binary files differnew file mode 100644 index 0000000000..75a9aaa927 --- /dev/null +++ b/native_client_sdk/src/libraries/xray/xray.odt diff --git a/native_client_sdk/src/libraries/xray/xray_priv.h b/native_client_sdk/src/libraries/xray/xray_priv.h new file mode 100644 index 0000000000..2f1150143b --- /dev/null +++ b/native_client_sdk/src/libraries/xray/xray_priv.h @@ -0,0 +1,117 @@ +/* Copyright (c) 2013 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. + */ + + +/* XRay -- a simple profiler for Native Client */ + +/* This header file is the private internal interface. */ + +#ifndef LIBRARIES_XRAY_XRAY_PRIV_H_ +#define LIBRARIES_XRAY_XRAY_PRIV_H_ + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include "xray/xray.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(XRAY) + +#define XRAY_FORCE_INLINE __attribute__((always_inline)) + +#define XRAY_TRACE_STACK_SIZE (256) +#define XRAY_TRACE_ANNOTATION_LENGTH (1024) +#define XRAY_TRACE_BUFFER_SIZE (1048576) +#define XRAY_ANNOTATION_STACK_SIZE ((XRAY_TRACE_STACK_SIZE) * \ + (XRAY_TRACE_ANNOTATION_LENGTH)) +#define XRAY_STRING_POOL_NODE_SIZE (32768) +#define XRAY_FRAME_MARKER (0xFFFFFFFF) +#define XRAY_NULL_ANNOTATION (0x0) +#define XRAY_FUNCTION_ALIGNMENT_BITS (4) +#define XRAY_ADDR_MASK (0xFFFFFF00) +#define XRAY_ADDR_SHIFT (4) +#define XRAY_DEPTH_MASK (0x000000FF) +#define XRAY_SYMBOL_TABLE_MAX_RATIO (0.66f) +#define XRAY_LINE_SIZE (1024) +#define XRAY_MAX_FRAMES (60) +#define XRAY_MAX_LABEL (64) +#define XRAY_DEFAULT_SYMBOL_TABLE_SIZE (4096) +#define XRAY_SYMBOL_POOL_NODE_SIZE (1024) +#define XRAY_GUARD_VALUE (0x12345678) +#define XRAY_EXTRACT_ADDR(x) (((x) & XRAY_ADDR_MASK) >> XRAY_ADDR_SHIFT) +#define XRAY_EXTRACT_DEPTH(x) ((x) & XRAY_DEPTH_MASK) +#define XRAY_PACK_ADDR(x) (((x) << XRAY_ADDR_SHIFT) & XRAY_ADDR_MASK) +#define XRAY_PACK_DEPTH(x) ((x) & XRAY_DEPTH_MASK) +#define XRAY_PACK_DEPTH_ADDR(d, a) (XRAY_PACK_DEPTH(d) | XRAY_PACK_ADDR(a)) +#define XRAY_ALIGN64 __attribute((aligned(64))) + +struct XRayStringPool; +struct XRayHashTable; +struct XRaySymbolPool; +struct XRaySymbol; +struct XRaySymbolTable; + +/* Important: don't instrument xray itself, so use */ +/* XRAY_NO_INSTRUMENT on all xray functions */ + +XRAY_NO_INSTRUMENT char* XRayStringPoolAppend(struct XRayStringPool* pool, + const char* src); +XRAY_NO_INSTRUMENT struct XRayStringPool* XRayStringPoolCreate(); +XRAY_NO_INSTRUMENT void XRayStringPoolFree(struct XRayStringPool* pool); + +XRAY_NO_INSTRUMENT void* XRayHashTableLookup(struct XRayHashTable* table, + uint32_t addr); +XRAY_NO_INSTRUMENT void* XRayHashTableInsert(struct XRayHashTable* table, + void* data, uint32_t addr); +XRAY_NO_INSTRUMENT void* XRayHashTableAtIndex( + struct XRayHashTable* table, int i); +XRAY_NO_INSTRUMENT int XRayHashTableGetSize(struct XRayHashTable* table); +XRAY_NO_INSTRUMENT struct XRayHashTable* XRayHashTableCreate(int size); +XRAY_NO_INSTRUMENT void XRayHashTableFree(struct XRayHashTable* table); +XRAY_NO_INSTRUMENT void XRayHashTableHisto(FILE* f); + +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolPoolAlloc( + struct XRaySymbolPool* sympool); +XRAY_NO_INSTRUMENT struct XRaySymbolPool* XRaySymbolPoolCreate(); +XRAY_NO_INSTRUMENT void XRaySymbolPoolFree(struct XRaySymbolPool* sympool); + +XRAY_NO_INSTRUMENT const char* XRayDemangle(char* demangle, size_t buffersize, + const char* symbol); + +XRAY_NO_INSTRUMENT const char* XRaySymbolGetName(struct XRaySymbol* symbol); +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolCreate( + struct XRaySymbolPool* sympool, const char* name); +XRAY_NO_INSTRUMENT void XRaySymbolFree(struct XRaySymbol* symbol); +XRAY_NO_INSTRUMENT int XRaySymbolCount(struct XRaySymbolTable* symtab); + +XRAY_NO_INSTRUMENT void XRaySymbolTableParseMapfile( + struct XRaySymbolTable* symbols, const char* mapfilename); +XRAY_NO_INSTRUMENT int XRaySymbolTableGetSize(struct XRaySymbolTable* symtab); +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolTableLookup( + struct XRaySymbolTable* symbols, uint32_t addr); +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolTableAtIndex( + struct XRaySymbolTable* symbols, int i); +XRAY_NO_INSTRUMENT struct XRaySymbolTable* XRaySymbolTableCreate(int size); +XRAY_NO_INSTRUMENT void XRaySymbolTableFree(struct XRaySymbolTable* symbtab); + +XRAY_NO_INSTRUMENT void* XRayMalloc(size_t t); +XRAY_NO_INSTRUMENT void XRayFree(void* data); +XRAY_NO_INSTRUMENT void XRaySetMaxStackDepth(int stack_depth); +XRAY_NO_INSTRUMENT int XRayGetLastFrame(); +XRAY_NO_INSTRUMENT void XRayDisableCapture(); +XRAY_NO_INSTRUMENT void XRayEnableCapture(); +XRAY_NO_INSTRUMENT void XRayLoadMapfile(const char* mapfilename); + +#endif /* defined(XRAY) */ + +#ifdef __cplusplus +} +#endif + +#endif /* LIBRARIES_XRAY_XRAY_PRIV_H_ */ diff --git a/native_client_sdk/src/project_templates/README b/native_client_sdk/src/project_templates/README deleted file mode 100644 index 5aca398aba..0000000000 --- a/native_client_sdk/src/project_templates/README +++ /dev/null @@ -1,45 +0,0 @@ -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 deleted file mode 100644 index 6836f4ebcc..0000000000 --- a/native_client_sdk/src/project_templates/c/build.scons +++ /dev/null @@ -1,15 +0,0 @@ -#! -*- python -*- -# -# 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. - -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 deleted file mode 100644 index 7f25bd659e..0000000000 --- a/native_client_sdk/src/project_templates/c/project_file.c +++ /dev/null @@ -1,227 +0,0 @@ -/** @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 deleted file mode 100644 index 04d661b84c..0000000000 --- a/native_client_sdk/src/project_templates/cc/build.scons +++ /dev/null @@ -1,16 +0,0 @@ -#! -*- python -*- -# -# 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. - -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 deleted file mode 100644 index c0123fe128..0000000000 --- a/native_client_sdk/src/project_templates/cc/project_file.cc +++ /dev/null @@ -1,87 +0,0 @@ -/// @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 deleted file mode 100644 index db3bc1a74a..0000000000 --- a/native_client_sdk/src/project_templates/html/project_file.html +++ /dev/null @@ -1,95 +0,0 @@ -<!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, - its 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 deleted file mode 100755 index 0201fa7002..0000000000 --- a/native_client_sdk/src/project_templates/init_project.py +++ /dev/null @@ -1,505 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""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, - nacl_sdk_root=None, 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_dir = None - 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.__nacl_sdk_root = nacl_sdk_root - 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 = self.__nacl_sdk_root - 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', - os.path.dirname(os.path.dirname(script_dir))) - 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, - nacl_sdk_root=sdk_root_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 deleted file mode 100755 index ae1d0b291f..0000000000 --- a/native_client_sdk/src/project_templates/init_project_test.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 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. - -"""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 __init__(self): - unittest.TestCase.__init__(self) - self.os_mock = None - self.fileinput_mock = None - self.sys_mock = None - self.shutil_mock = None - - 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, - self.nacl_src_dir, os_resource=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 deleted file mode 100755 index 17e9350017..0000000000 --- a/native_client_sdk/src/project_templates/scons +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# -# 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. - -readonly SCRIPT_DIR="$(dirname "$0")" -readonly SCRIPT_DIR_ABS="$(cd "${SCRIPT_DIR}" ; pwd -P)" -readonly SRC_DIR="$(dirname $(dirname $(dirname ${SCRIPT_DIR_ABS})))" - -# 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}" - -SCONS_DIR="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1" - -if [ ! -f ${SCONS_DIR}/script/scons ]; then - SCONS_DIR="${SRC_DIR}/third_party/scons-2.0.1" -fi - -BASE_SCRIPT="${SCONS_DIR}/script/scons" - -export SCONS_LIB_DIR="${SCONS_DIR}/engine" -export PYTHONPATH="${SCONS_LIB_DIR}" -export PYTHONPATH="${PYTHONPATH}:${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/src" - -"${BASE_SCRIPT}" --file=build.scons \ - --site-dir="${SCRIPT_DIR_ABS}/../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 deleted file mode 100755 index d56f2456cc..0000000000 --- a/native_client_sdk/src/project_templates/scons.bat +++ /dev/null @@ -1,50 +0,0 @@ -@echo off - -:: 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. - -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_DIR=%NACL_PLATFORM_DIR%\third_party\scons-2.0.1 -if exist %SCONS_DIR% goto gotscons -set SCONS_DIR=%~dp0..\..\..\third_party\scons-2.0.1 -:gotscons - -set SCONS_LIB_DIR=%SCONS_DIR%\engine -set PYTHONPATH=%SCONS_LIB_DIR%;%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\src - -set BASE_SCRIPT=%SCONS_DIR%\script\scons - -:: Run the included copy of scons. -python -O -OO %BASE_SCRIPT% ^ ---warn no-visual-c-missing ^ ---file=build.scons ^ ---site-dir="%~dp0..\build_tools\nacl_sdk_scons" %* - -:end diff --git a/native_client_sdk/src/project_templates/test.scons b/native_client_sdk/src/project_templates/test.scons deleted file mode 100644 index 56c3ac2731..0000000000 --- a/native_client_sdk/src/project_templates/test.scons +++ /dev/null @@ -1,26 +0,0 @@ -#! -*- python -*- -# -# Copyright (c) 2010 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. - -"""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 deleted file mode 100644 index 05f5169222..0000000000 --- a/native_client_sdk/src/project_templates/vs/project_file.sln +++ /dev/null @@ -1,20 +0,0 @@ - -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 deleted file mode 100644 index d2b0f253e8..0000000000 --- a/native_client_sdk/src/project_templates/vs/project_file.vcproj +++ /dev/null @@ -1,184 +0,0 @@ -<?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 && $(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 && $(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/tools/common.mk b/native_client_sdk/src/tools/common.mk index 058af1b383..ae8604d48c 100644 --- a/native_client_sdk/src/tools/common.mk +++ b/native_client_sdk/src/tools/common.mk @@ -28,6 +28,7 @@ TOP_MAKE := $(word 1,$(MAKEFILE_LIST)) # Figure out which OS we are running on. # GETOS = python $(NACL_SDK_ROOT)/tools/getos.py +FIXDEPS = python $(NACL_SDK_ROOT)/tools/fix_deps.py OSNAME := $(shell $(GETOS)) @@ -77,10 +78,23 @@ else # TOOLCHAIN=all # Verify we selected a valid toolchain for this example # ifeq (,$(findstring $(TOOLCHAIN),$(VALID_TOOLCHAINS))) + +# Only fail to build if this is a top-level make. When building recursively, we +# don't care if an example can't build with this toolchain. +ifeq ($(MAKELEVEL),0) $(warning Availbile choices are: $(VALID_TOOLCHAINS)) $(error Can not use TOOLCHAIN=$(TOOLCHAIN) on this example.) +else + +# Dummy targets for recursive make with unsupported toolchain... +.PHONY: all clean install +all: +clean: +install: + endif +else # TOOLCHAIN is valid... # # Build Configuration @@ -234,7 +248,7 @@ clean: # $3 = Extra Settings # define DEPEND_RULE -ifndef $(IGNORE_DEPS) +ifndef IGNORE_DEPS .PHONY: rebuild_$(1) rebuild_$(1) :| $(STAMPDIR)/dir.stamp @@ -370,7 +384,7 @@ endif # # Assign a sensible default to CHROME_PATH. # -CHROME_PATH ?= $(shell python $(NACL_SDK_ROOT)/tools/getos.py --chrome 2> $(DEV_NULL)) +CHROME_PATH ?= $(shell $(GETOS) --chrome 2> $(DEV_NULL)) # # Verify we can find the Chrome executable if we need to launch it. @@ -423,7 +437,7 @@ run_package: check_for_chrome all $(CHROME_PATH) --load-and-launch-app=$(CURDIR) $(CHROME_ARGS) -SYSARCH = $(shell python $(NACL_SDK_ROOT)/tools/getos.py --nacl-arch) +SYSARCH = $(shell $(GETOS) --nacl-arch) GDB_ARGS += -D $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/$(SYSARCH)-nacl-gdb GDB_ARGS += -D $(abspath $(OUTDIR))/$(TARGET)_$(SYSARCH).nexe @@ -443,4 +457,6 @@ DEBUG: debug LAUNCH: run RUN: run +endif # TOOLCHAIN is valid... + endif # TOOLCHAIN=all diff --git a/native_client_sdk/src/tools/create_html.py b/native_client_sdk/src/tools/create_html.py index c0d1eb43ce..8634d4a4a2 100755 --- a/native_client_sdk/src/tools/create_html.py +++ b/native_client_sdk/src/tools/create_html.py @@ -136,6 +136,8 @@ def CreateHTML(filenames, options): cmd = [create_nmf, '-s', staging, '-o', nmf] + filenames if options.verbose: cmd.append('-v') + if options.debug_libs: + cmd.append('--debug-libs') Log(cmd) try: subprocess.check_call(cmd) @@ -158,6 +160,8 @@ def main(argv): parser = optparse.OptionParser(usage, description=description, epilog=epilog) parser.add_option('-v', '--verbose', action='store_true', help='Verbose output') + parser.add_option('-d', '--debug-libs', action='store_true', + help='When calling create_nmf request debug libaries') parser.add_option('-o', '--output', dest='output', help='Name of html file to write (default is ' 'input name with .html extension)', diff --git a/native_client_sdk/src/tools/fix_deps.py b/native_client_sdk/src/tools/fix_deps.py new file mode 100755 index 0000000000..a10feacc11 --- /dev/null +++ b/native_client_sdk/src/tools/fix_deps.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# Copyright (c) 2013 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. + +"""Fixup GCC-generated dependency files. + +Modify GCC generated dependency files in-place so they are more suitable +for including in a GNU Makefile. Without the fixups, deleting or renaming +headers can cause the build to be broken. See: +http://mad-scientist.net/make/autodep.html for more details of the problem. +""" + +import os +import optparse +import sys + +TAG_LINE = '# Updated by fix_deps.py\n' + + +class Error(Exception): + pass + + +def ParseLine(line, new_target): + """Parse one line of a GCC-generated deps file. + + Each line contains an optional target and then a list + of space seperated dependencies. Spaces within filenames + are escaped with a backslash. + """ + filenames = [] + + if new_target and ':' in line: + line = line.split(':', 1)[1] + + line = line.strip() + line = line.rstrip('\\') + + while True: + # Find the next non-escaped space + line = line.strip() + pos = line.find(' ') + while pos > 0 and line[pos-1] == '\\': + pos = line.find(' ', pos+1) + + if pos == -1: + filenames.append(line) + break + filenames.append(line[:pos]) + line = line[pos+1:] + + return filenames + + +def FixupDepFile(filename): + if not os.path.exists(filename): + raise Error('File not found: %s' % filename) + + outlines = [TAG_LINE] + deps = [] + new_target = True + with open(filename) as infile: + for line in infile: + if line == TAG_LINE: + raise Error('Already processed: %s' % filename) + outlines.append(line) + deps += ParseLine(line, new_target) + new_target = line.endswith('\\') + + # For every depenency found output a dummy target with no rules + for dep in deps: + outlines.append('%s:\n' % dep) + + with open(filename, 'w') as outfile: + for line in outlines: + outfile.write(line) + + +def main(argv): + usage = "usage: %prog [options] <dep_file ...>" + parser = optparse.OptionParser(usage=usage, description=__doc__) + args = parser.parse_args(argv)[1] + if not args: + raise parser.error('expected one or more files as arguments') + for arg in args: + FixupDepFile(arg) + + +if __name__ == '__main__': + try: + sys.exit(main(sys.argv[1:])) + except Error as e: + sys.stderr.write('%s: %s\n' % (os.path.basename(__file__), e)) + sys.exit(1) diff --git a/native_client_sdk/src/tools/getos.py b/native_client_sdk/src/tools/getos.py index bd312b4b90..70971a67a8 100755 --- a/native_client_sdk/src/tools/getos.py +++ b/native_client_sdk/src/tools/getos.py @@ -69,7 +69,7 @@ def GetSDKVersion(): name, value = line.split(':', 1) if name == "Version": version = value.strip() - if name == "Revision": + if name == "Chrome Revision": revision = value.strip() if revision == None or version == None: diff --git a/native_client_sdk/src/tools/host_gcc.mk b/native_client_sdk/src/tools/host_gcc.mk index 61b4a7e322..601ec814ca 100644 --- a/native_client_sdk/src/tools/host_gcc.mk +++ b/native_client_sdk/src/tools/host_gcc.mk @@ -14,8 +14,8 @@ # We use the C++ compiler for everything and then use the -Wl,-as-needed flag # in the linker to drop libc++ unless it's actually needed. # -HOST_CC ?= gcc -HOST_CXX ?= g++ +HOST_CC ?= $(NACL_COMPILER_PREFIX) gcc +HOST_CXX ?= $(NACL_COMPILER_PREFIX) g++ HOST_LINK ?= g++ HOST_LIB ?= ar HOST_STRIP ?= strip @@ -41,15 +41,19 @@ define C_COMPILER_RULE -include $(call SRC_TO_DEP,$(1)) $(call SRC_TO_OBJ,$(1)): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CC ,$$@,$(HOST_CC) -o $$@ -c $$< -fPIC $(POSIX_FLAGS) $(2) $(LINUX_FLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1)) endef define CXX_COMPILER_RULE -include $(call SRC_TO_DEP,$(1)) $(call SRC_TO_OBJ,$(1)): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CXX ,$$@,$(HOST_CXX) -o $$@ -c $$< -fPIC $(POSIX_FLAGS) $(2) $(LINUX_FLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1)) endef - +# +# Compile Macro +# # $1 = Source Name # $2 = POSIX Compile Flags # $3 = VC Flags (unused) diff --git a/native_client_sdk/src/tools/nacl_gcc.mk b/native_client_sdk/src/tools/nacl_gcc.mk index 383f0f45c3..86c867913d 100644 --- a/native_client_sdk/src/tools/nacl_gcc.mk +++ b/native_client_sdk/src/tools/nacl_gcc.mk @@ -22,27 +22,29 @@ LD_ARM := -L$(NACL_SDK_ROOT)/lib/$(TOOLCHAIN)_arm/$(CONFIG) # We always link with the C++ compiler but include -Wl,-as-needed flag # in LD_FLAGS so the linker should drop libc++ unless it's actually needed. # -X86_32_CC ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-gcc -X86_32_CXX ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-g++ -X86_32_LINK ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-g++ -X86_32_LIB ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-ar -X86_32_STRIP ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-strip -X86_32_NM ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-nm - -X86_64_CC ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-gcc -X86_64_CXX ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-g++ -X86_64_LINK ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-g++ -X86_64_LIB ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-ar -X86_64_STRIP ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-strip -X86_64_NM ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-nm - -ARM_CC ?= $(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-gcc -ARM_CXX ?= $(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-g++ -ARM_LINK ?= $(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-g++ -ARM_LIB ?= $(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-ar -ARM_STRIP ?= $(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-strip -ARM_NM ?= $(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-nm - +X86_TC_BIN ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin +ARM_TC_BIN ?= $(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin + +X86_32_CC ?= $(NACL_COMPILER_PREFIX) $(X86_TC_BIN)/i686-nacl-gcc +X86_32_CXX ?= $(NACL_COMPILER_PREFIX) $(X86_TC_BIN)/i686-nacl-g++ +X86_32_LINK ?= $(X86_TC_BIN)/i686-nacl-g++ +X86_32_LIB ?= $(X86_TC_BIN)/i686-nacl-ar +X86_32_STRIP ?= $(X86_TC_BIN)/i686-nacl-strip +X86_32_NM ?= $(X86_TC_BIN)/i686-nacl-nm + +X86_64_CC ?= $(NACL_COMPILER_PREFIX) $(X86_TC_BIN)/x86_64-nacl-gcc +X86_64_CXX ?= $(NACL_COMPILER_PREFIX) $(X86_TC_BIN)/x86_64-nacl-g++ +X86_64_LINK ?= $(X86_TC_BIN)/x86_64-nacl-g++ +X86_64_LIB ?= $(X86_TC_BIN)/x86_64-nacl-ar +X86_64_STRIP ?= $(X86_TC_BIN)/x86_64-nacl-strip +X86_64_NM ?= $(X86_TC_BIN)/x86_64-nacl-nm + +ARM_CC ?= $(NACL_COMPILER_PREFIX) $(ARM_TC_BIN)/arm-nacl-gcc +ARM_CXX ?= $(NACL_COMPILER_PREFIX) $(ARM_TC_BIN)/arm-nacl-g++ +ARM_LINK ?= $(ARM_TC_BIN)/arm-nacl-g++ +ARM_LIB ?= $(ARM_TC_BIN)/arm-nacl-ar +ARM_STRIP ?= $(ARM_TC_BIN)/arm-nacl-strip +ARM_NM ?= $(ARM_TC_BIN)/arm-nacl-nm # Architecture-specific flags X86_32_CFLAGS ?= @@ -53,6 +55,9 @@ X86_32_CXXFLAGS ?= X86_64_CXXFLAGS ?= ARM_CXXFLAGS ?= +X86_32_LDFLAGS ?= -Wl,-Map,$(OUTDIR)/$(TARGET)_x86_32.map +X86_64_LDFLAGS ?= -Wl,-Map,$(OUTDIR)/$(TARGET)_x86_64.map +ARM_LDFLAGS ?= -Wl,-Map,$(OUTDIR)/$(TARGET)_arm.map # # Compile Macro @@ -64,52 +69,64 @@ define C_COMPILER_RULE -include $(call SRC_TO_DEP,$(1),_x86_32) $(call SRC_TO_OBJ,$(1),_x86_32): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CC ,$$@,$(X86_32_CC) -o $$@ -c $$< $(POSIX_FLAGS) $(2) $(NACL_CFLAGS) $(X86_32_CFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_x86_32) -include $(call SRC_TO_DEP,$(1),_x86_64) $(call SRC_TO_OBJ,$(1),_x86_64): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CC ,$$@,$(X86_64_CC) -o $$@ -c $$< $(POSIX_FLAGS) $(2) $(NACL_CFLAGS) $(X86_64_CFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_x86_64) -include $(call SRC_TO_DEP,$(1),_arm) $(call SRC_TO_OBJ,$(1),_arm): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CC ,$$@,$(ARM_CC) -o $$@ -c $$< $(POSIX_FLAGS) $(2) $(NACL_CFLAGS) $(ARM_CFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_arm) -include $(call SRC_TO_DEP,$(1),_x86_32_pic) $(call SRC_TO_OBJ,$(1),_x86_32_pic): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CC ,$$@,$(X86_32_CC) -o $$@ -c $$< -fPIC $(POSIX_FLAGS) $(2) $(NACL_CFLAGS) $(X86_32_CFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_x86_32_pic) -include $(call SRC_TO_DEP,$(1),_x86_64_pic) $(call SRC_TO_OBJ,$(1),_x86_64_pic): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CC ,$$@,$(X86_64_CC) -o $$@ -c $$< -fPIC $(POSIX_FLAGS) $(2) $(NACL_CFLAGS) $(X86_64_CFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_x86_64_pic) -include $(call SRC_TO_DEP,$(1),_arm_pic) $(call SRC_TO_OBJ,$(1),_arm_pic): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CC ,$$@,$(ARM_CC) -o $$@ -c $$< -fPIC $(POSIX_FLAGS) $(2) $(NACL_CFLAGS) $(ARM_CFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_arm_pic) endef define CXX_COMPILER_RULE -include $(call SRC_TO_DEP,$(1),_x86_32) $(call SRC_TO_OBJ,$(1),_x86_32): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CXX ,$$@,$(X86_32_CXX) -o $$@ -c $$< $(POSIX_FLAGS) $(2) $(NACL_CXXFLAGS) $(X86_32_CXXFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_x86_32) -include $(call SRC_TO_DEP,$(1),_x86_64) $(call SRC_TO_OBJ,$(1),_x86_64): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CXX ,$$@,$(X86_64_CXX) -o $$@ -c $$< $(POSIX_FLAGS) $(2) $(NACL_CXXFLAGS) $(X86_64_CXXFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_x86_64) -include $(call SRC_TO_DEP,$(1),_arm) $(call SRC_TO_OBJ,$(1),_arm): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CXX ,$$@,$(ARM_CXX) -o $$@ -c $$< $(POSIX_FLAGS) $(2) $(NACL_CXXFLAGS) $(ARM_CXXFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_arm) -include $(call SRC_TO_DEP,$(1),_x86_32_pic) $(call SRC_TO_OBJ,$(1),_x86_32_pic): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CXX ,$$@,$(X86_32_CXX) -o $$@ -c $$< -fPIC $(POSIX_FLAGS) $(2) $(NACL_CXXFLAGS) $(X86_32_CXXFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_x86_32_pic) -include $(call SRC_TO_DEP,$(1),_x86_64_pic) $(call SRC_TO_OBJ,$(1),_x86_64_pic): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CXX ,$$@,$(X86_64_CXX) -o $$@ -c $$< -fPIC $(POSIX_FLAGS) $(2) $(NACL_CXXFLAGS) $(X86_64_CXXFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_x86_64_pic) -include $(call SRC_TO_DEP,$(1),_arm_pic) $(call SRC_TO_OBJ,$(1),_arm_pic): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CXX ,$$@,$(ARM_CXX) -o $$@ -c $$< -fPIC $(POSIX_FLAGS) $(2) $(NACL_CXXFLAGS) $(ARM_CXXFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_arm_pic) endef @@ -249,19 +266,19 @@ define LINKER_RULE ifneq (,$(findstring x86_32,$(ARCHES))) all: $(OUTDIR)/$(1)_x86_32.nexe $(OUTDIR)/$(1)_x86_32.nexe: $(foreach src,$(2),$(call SRC_TO_OBJ,$(src),_x86_32)) $(foreach dep,$(4),$(STAMPDIR)/$(dep).stamp) - $(call LOG,LINK,$$@,$(X86_32_LINK) -o $$@ $$(filter %.o,$$^) $(NACL_LDFLAGS) $(foreach path,$(6),-L$(path)/$(TOOLCHAIN)_x86_32/$(CONFIG)) $(foreach lib,$(3),-l$(lib)) $(5)) + $(call LOG,LINK,$$@,$(X86_32_LINK) -o $$@ $$(filter %.o,$$^) $(NACL_LDFLAGS) $(X86_32_LDFLAGS) $(foreach path,$(6),-L$(path)/$(TOOLCHAIN)_x86_32/$(CONFIG)) $(foreach lib,$(3),-l$(lib)) $(5)) endif ifneq (,$(findstring x86_64,$(ARCHES))) all: $(OUTDIR)/$(1)_x86_64.nexe $(OUTDIR)/$(1)_x86_64.nexe: $(foreach src,$(2),$(call SRC_TO_OBJ,$(src),_x86_64)) $(foreach dep,$(4),$(STAMPDIR)/$(dep).stamp) - $(call LOG,LINK,$$@,$(X86_64_LINK) -o $$@ $$(filter %.o,$$^) $(NACL_LDFLAGS) $(foreach path,$(6),-L$(path)/$(TOOLCHAIN)_x86_64/$(CONFIG)) $(foreach lib,$(3),-l$(lib)) $(5)) + $(call LOG,LINK,$$@,$(X86_64_LINK) -o $$@ $$(filter %.o,$$^) $(NACL_LDFLAGS) $(X86_64_LDFLAGS) $(foreach path,$(6),-L$(path)/$(TOOLCHAIN)_x86_64/$(CONFIG)) $(foreach lib,$(3),-l$(lib)) $(5)) endif ifneq (,$(findstring arm,$(ARCHES))) all: $(OUTDIR)/$(1)_arm.nexe $(OUTDIR)/$(1)_arm.nexe: $(foreach src,$(2),$(call SRC_TO_OBJ,$(src),_arm)) $(foreach dep,$(4),$(STAMPDIR)/$(dep).stamp) - $(call LOG,LINK,$$@,$(ARM_LINK) -o $$@ $$(filter %.o,$$^) $(NACL_LDFLAGS) $(foreach path,$(6),-L$(path)/$(TOOLCHAIN)_arm/$(CONFIG)) $(foreach lib,$(3),-l$(lib)) $(5)) + $(call LOG,LINK,$$@,$(ARM_LINK) -o $$@ $$(filter %.o,$$^) $(NACL_LDFLAGS) $(ARM_LDFLAGS) $(foreach path,$(6),-L$(path)/$(TOOLCHAIN)_arm/$(CONFIG)) $(foreach lib,$(3),-l$(lib)) $(5)) endif endef @@ -368,12 +385,14 @@ ARCH_SUFFIXES := $(foreach arch,$(ARCHES),_$(arch).nexe) NMF := python $(NACL_SDK_ROOT)/tools/create_nmf.py ifeq ($(CONFIG),Debug) NMF_FLAGS += --debug-libs +HTML_FLAGS += --debug-libs endif +EXECUTABLES=$(foreach arch,$(ARCH_SUFFIXES),$(OUTDIR)/$(1)$(arch)) $(GLIBC_SO_LIST) define NMF_RULE all: $(OUTDIR)/$(1).nmf -$(OUTDIR)/$(1).nmf: $(foreach arch,$(ARCH_SUFFIXES),$(OUTDIR)/$(1)$(arch)) $(GLIBC_SO_LIST) +$(OUTDIR)/$(1).nmf: $(EXECUTABLES) $(call LOG,CREATE_NMF,$$@,$(NMF) $(NMF_FLAGS) -o $$@ $$^ $(GLIBC_PATHS) -s $(OUTDIR) $(2) $(GLIBC_REMAP)) endef @@ -384,6 +403,6 @@ CREATE_HTML := python $(NACL_SDK_ROOT)/tools/create_html.py define HTML_RULE all: $(OUTDIR)/$(1).html -$(OUTDIR)/$(1).html: $(foreach arch,$(ARCH_SUFFIXES),$(OUTDIR)/$(1)$(arch)) $(GLIBC_SO_LIST) - $(call LOG,CREATE_HTML,$$@,$(CREATE_HTML) -o $$@ $$^) +$(OUTDIR)/$(1).html: $(EXECUTABLES) + $(call LOG,CREATE_HTML,$$@,$(CREATE_HTML) $(HTML_FLAGS) -o $$@ $$^) endef diff --git a/native_client_sdk/src/tools/nacl_llvm.mk b/native_client_sdk/src/tools/nacl_llvm.mk index fe9762bf26..f6301e5e5d 100644 --- a/native_client_sdk/src/tools/nacl_llvm.mk +++ b/native_client_sdk/src/tools/nacl_llvm.mk @@ -7,15 +7,16 @@ # http://www.gnu.org/software/make/manual/make.html # - # # Paths to Tools # -PNACL_CC ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/newlib/bin/pnacl-clang -c -PNACL_CXX ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/newlib/bin/pnacl-clang++ -c -PNACL_LINK ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/newlib/bin/pnacl-clang++ -PNACL_LIB ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/newlib/bin/pnacl-ar -PNACL_STRIP ?= $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/newlib/bin/pnacl-finalize +PNACL_BIN = $(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/newlib/bin +PNACL_CC ?= $(PNACL_BIN)/pnacl-clang -c +PNACL_CXX ?= $(PNACL_BIN)/pnacl-clang++ -c +PNACL_LINK ?= $(PNACL_BIN)/pnacl-clang++ +PNACL_LIB ?= $(PNACL_BIN)/pnacl-ar +PNACL_STRIP ?= $(PNACL_BIN)/pnacl-strip +PNACL_FINALIZE ?= $(PNACL_BIN)/pnacl-finalize # # Compile Macro @@ -28,12 +29,14 @@ define C_COMPILER_RULE -include $(call SRC_TO_DEP,$(1),_pnacl) $(call SRC_TO_OBJ,$(1),_pnacl): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CC ,$$@,$(PNACL_CC) -o $$@ -c $$< $(POSIX_FLAGS) $(2) $(NACL_CFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_pnacl) endef define CXX_COMPILER_RULE --include $(call SRC_TO_DEP,$(1)) +-include $(call SRC_TO_DEP,$(1),_pnacl) $(call SRC_TO_OBJ,$(1),_pnacl): $(1) $(TOP_MAKE) | $(dir $(call SRC_TO_OBJ,$(1)))dir.stamp $(call LOG,CXX ,$$@,$(PNACL_CXX) -o $$@ -c $$< $(POSIX_FLAGS) $(2) $(NACL_CFLAGS)) + @$(FIXDEPS) $(call SRC_TO_DEP,$(1),_pnacl) endef @@ -91,9 +94,10 @@ endef # $6 = Other Linker Args # define LINKER_RULE -all: $(1) -$(1): $(2) $(foreach dep,$(4),$(STAMPDIR)/$(dep).stamp) - $(call LOG,LINK,$$@,$(PNACL_LINK) -o $(1) $(2) $(foreach path,$(5),-L$(path)/pnacl/$(CONFIG)) $(foreach lib,$(3),-l$(lib)) $(6)) +all: $(1).pexe +$(1).pexe: $(2) $(foreach dep,$(4),$(STAMPDIR)/$(dep).stamp) + $(call LOG,LINK,$(1).bc,$(PNACL_LINK) -o $(1).bc $(2) $(foreach path,$(5),-L$(path)/pnacl/$(CONFIG)) $(foreach lib,$(3),-l$(lib)) $(6)) + $(call LOG,FINALIZE,$(1).pexe,$(PNACL_FINALIZE) -o $(1).pexe $(1).bc) endef @@ -108,20 +112,25 @@ endef # $6 = VC Linker Switches # define LINK_RULE -$(call LINKER_RULE,$(OUTDIR)/$(1).pexe,$(foreach src,$(2),$(call SRC_TO_OBJ,$(src),_pnacl)),$(filter-out pthread,$(3)),$(4),$(LIB_PATHS),$(5)) +$(call LINKER_RULE,$(OUTDIR)/$(1),$(foreach src,$(2),$(call SRC_TO_OBJ,$(src),_pnacl)),$(filter-out pthread,$(3)),$(4),$(LIB_PATHS),$(5)) endef # # Strip Macro # +# NOTE: pnacl-strip does not currently support stripping finalized pexes (in a +# sense, they are already stripped). So we just copy the file instead. +# +# See https://code.google.com/p/nativeclient/issues/detail?id=3534 +# # $1 = Target Name # $2 = Input Name # define STRIP_RULE all: $(OUTDIR)/$(1).pexe $(OUTDIR)/$(1).pexe: $(OUTDIR)/$(2).pexe - $(call LOG,STRIP,$$@,$(PNACL_STRIP) -o $$@ $$^) + $(CP) $$^ $$@ endef diff --git a/native_client_sdk/src/tools/tests/fix_deps_test.py b/native_client_sdk/src/tools/tests/fix_deps_test.py new file mode 100755 index 0000000000..d02f8ac12e --- /dev/null +++ b/native_client_sdk/src/tools/tests/fix_deps_test.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. +import os +import sys +import tempfile +import unittest + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +PARENT_DIR = os.path.dirname(SCRIPT_DIR) +DATA_DIR = os.path.join(SCRIPT_DIR, 'data') +CHROME_SRC = os.path.dirname(os.path.dirname(os.path.dirname(PARENT_DIR))) +MOCK_DIR = os.path.join(CHROME_SRC, "third_party", "pymock") + +# For the mock library +sys.path.append(MOCK_DIR) +sys.path.append(PARENT_DIR) + +import fix_deps +import mock + + +class TestFixDeps(unittest.TestCase): + def setUp(self): + self.tempfile = None + + def tearDown(self): + if self.tempfile: + os.remove(self.tempfile) + + def testRequiresFile(self): + with mock.patch('sys.stderr'): + self.assertRaises(SystemExit, fix_deps.main, []) + + def testInvalidOption(self): + with mock.patch('sys.stderr'): + self.assertRaises(SystemExit, fix_deps.main, ['--foo', 'bar']) + + def testMissingFile(self): + with mock.patch('sys.stderr'): + self.assertRaises(fix_deps.Error, fix_deps.main, ['nonexistent.file']) + + def testAddsDeps(self): + self.tempfile = tempfile.mktemp("_sdktest") + with open(self.tempfile, 'w') as out: + out.write('foo.o: foo.c foo.h bar.h\n') + fix_deps.FixupDepFile(self.tempfile) + with open(self.tempfile) as infile: + contents = infile.read() + lines = contents.splitlines() + self.assertEqual(len(lines), 5) + self.assertTrue('foo.c:' in lines) + self.assertTrue('foo.h:' in lines) + self.assertTrue('bar.h:' in lines) + + def testSpacesInFilenames(self): + self.tempfile = tempfile.mktemp("_sdktest") + with open(self.tempfile, 'w') as out: + out.write('foo.o: foo\\ bar.h\n') + fix_deps.FixupDepFile(self.tempfile) + with open(self.tempfile) as infile: + contents = infile.read() + lines = contents.splitlines() + self.assertEqual(len(lines), 3) + self.assertEqual(lines[2], 'foo\\ bar.h:') + + def testColonInFilename(self): + self.tempfile = tempfile.mktemp("_sdktest") + with open(self.tempfile, 'w') as out: + out.write('foo.o: c:foo.c\\\n c:bar.h\n') + fix_deps.FixupDepFile(self.tempfile) + with open(self.tempfile) as infile: + contents = infile.read() + lines = contents.splitlines() + self.assertEqual(len(lines), 5) + self.assertEqual(lines[3], 'c:foo.c:') + self.assertEqual(lines[4], 'c:bar.h:') + + def testDoubleInvoke(self): + self.tempfile = tempfile.mktemp("_sdktest") + with open(self.tempfile, 'w') as out: + out.write('foo.o: foo\\ bar.h\n') + fix_deps.FixupDepFile(self.tempfile) + self.assertRaises(fix_deps.Error, fix_deps.FixupDepFile, self.tempfile) + + +if __name__ == '__main__': + unittest.main() diff --git a/native_client_sdk/src/tools/tests/getos_test.py b/native_client_sdk/src/tools/tests/getos_test.py index 3a6ad60c1e..99ffb95079 100755 --- a/native_client_sdk/src/tools/tests/getos_test.py +++ b/native_client_sdk/src/tools/tests/getos_test.py @@ -145,7 +145,7 @@ class TestGetosWithTempdir(TestCaseExtended): expected_version = (16, 196) with open(os.path.join(self.tempdir, 'README'), 'w') as out: out.write('Version: %s\n' % expected_version[0]) - out.write('Revision: %s\n' % expected_version[1]) + out.write('Chrome Revision: %s\n' % expected_version[1]) version = getos.GetSDKVersion() self.assertEqual(version, expected_version) |