Commit 0e151bf9 authored by noelallen@google.com's avatar noelallen@google.com

Support GLBIC example in SDK

This CL added two new examples for the NaCl SDK.  This only affects SDK bots since
the code is completely contained within native_client_sdk subtree.

Fix incorrectly placed ppapi headers.
Remove TODOs and cleanup create_nmf.py.
Add hello_world_glibc example
Add dlopen example.

BUG= 111224
Review URL: https://chromiumcodereview.appspot.com/9234043

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@120117 0039d316-1c4b-4281-b951-d872f2087c98
parent e4420b5e
...@@ -16,6 +16,7 @@ and whether it should upload an SDK to file storage (GSTORE) ...@@ -16,6 +16,7 @@ and whether it should upload an SDK to file storage (GSTORE)
""" """
# std python includes # std python includes
import optparse
import os import os
import subprocess import subprocess
import sys import sys
...@@ -24,7 +25,6 @@ import sys ...@@ -24,7 +25,6 @@ import sys
import build_utils import build_utils
import lastchange import lastchange
# Create the various paths of interest # Create the various paths of interest
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
SDK_SRC_DIR = os.path.dirname(SCRIPT_DIR) SDK_SRC_DIR = os.path.dirname(SCRIPT_DIR)
...@@ -116,21 +116,25 @@ def CopyDir(src, dst, excludes=['.svn']): ...@@ -116,21 +116,25 @@ def CopyDir(src, dst, excludes=['.svn']):
print "cp -r %s %s" % (src, dst) print "cp -r %s %s" % (src, dst)
oshelpers.Copy(args) oshelpers.Copy(args)
def RemoveDir(dst): def RemoveDir(dst):
"""Remove the provided path.""" """Remove the provided path."""
print "rm -fr " + dst print "rm -fr " + dst
oshelpers.Remove(['-fr', dst]) oshelpers.Remove(['-fr', dst])
def MakeDir(dst): def MakeDir(dst):
"""Create the path including all parent directories as needed.""" """Create the path including all parent directories as needed."""
print "mkdir -p " + dst print "mkdir -p " + dst
oshelpers.Mkdir(['-p', dst]) oshelpers.Mkdir(['-p', dst])
def MoveDir(src, dst): def MoveDir(src, dst):
"""Move the path src to dst.""" """Move the path src to dst."""
print "mv -fr %s %s" % (src, dst) print "mv -fr %s %s" % (src, dst)
oshelpers.Move(['-f', src, dst]) oshelpers.Move(['-f', src, dst])
def BuildOutputDir(*paths): def BuildOutputDir(*paths):
return os.path.join(OUT_DIR, *paths) return os.path.join(OUT_DIR, *paths)
...@@ -190,6 +194,7 @@ def GetBuildArgs(tcname, tcpath, arch, xarch=None): ...@@ -190,6 +194,7 @@ def GetBuildArgs(tcname, tcpath, arch, xarch=None):
args.append('--nacl_glibc') args.append('--nacl_glibc')
return args return args
header_map = { header_map = {
'newlib': { 'newlib': {
'pthread.h': 'src/untrusted/pthread/pthread.h', 'pthread.h': 'src/untrusted/pthread/pthread.h',
...@@ -278,17 +283,39 @@ def InstallHeaders(tc_dst_inc, pepper_ver, tc_name): ...@@ -278,17 +283,39 @@ def InstallHeaders(tc_dst_inc, pepper_ver, tc_name):
os.path.join(tc_dst_inc, 'KHR')) os.path.join(tc_dst_inc, 'KHR'))
def main(): def main(args):
parser = optparse.OptionParser()
# Modes
parser.add_option('--examples-only', help='Rebuild the examples.',
action='store_true', dest='examples_only', default=False)
parser.add_option('--skip-tar', help='Skip generating a tarball.',
action='store_true', dest='skip_tar', default=False)
parser.add_option('--archive', help='Force the archive step.',
action='store_true', dest='archive')
options, args = parser.parse_args(args[1:])
platform = getos.GetPlatform() platform = getos.GetPlatform()
arch = 'x86' arch = 'x86'
# the vars below are intended for debugging
skip_untar = 0 skip_examples = False
skip_build = 0 skip_untar = False
skip_tar = 0 skip_build = False
skip_examples = 0 skip_headers = False
skip_headers = 0 skip_tar = False
skip_make = 0 force_archive = options.archive
force_archive = 0
if options.examples_only:
skip_untar = True
skip_build = True
skip_headers = True
skip_tar = True
if options.skip_tar:
skip_tar = True
if options.archive and (options.examples_only or options.skip_tar):
parser.error('Incompatible arguments with archive.')
pepper_ver = build_utils.ChromeMajorVersion() pepper_ver = build_utils.ChromeMajorVersion()
clnumber = lastchange.FetchVersionInfo(None).revision clnumber = lastchange.FetchVersionInfo(None).revision
...@@ -369,7 +396,6 @@ def main(): ...@@ -369,7 +396,6 @@ def main():
RemoveDir(os.path.join(pepperdir, 'examples')) RemoveDir(os.path.join(pepperdir, 'examples'))
CopyDir(os.path.join(SDK_SRC_DIR, 'examples'), pepperdir) CopyDir(os.path.join(SDK_SRC_DIR, 'examples'), pepperdir)
tarname = 'naclsdk_' + platform + '.bz2' tarname = 'naclsdk_' + platform + '.bz2'
BuildStep('Tar Pepper Bundle') BuildStep('Tar Pepper Bundle')
if not skip_tar: if not skip_tar:
...@@ -382,7 +408,7 @@ def main(): ...@@ -382,7 +408,7 @@ def main():
BuildStep('Archive build') BuildStep('Archive build')
Archive(tarname) Archive(tarname)
if not skip_make: if not skip_examples:
BuildStep('Test Build Examples') BuildStep('Test Build Examples')
filelist = os.listdir(os.path.join(pepperdir, 'examples')) filelist = os.listdir(os.path.join(pepperdir, 'examples'))
for filenode in filelist: for filenode in filelist:
...@@ -398,4 +424,4 @@ def main(): ...@@ -398,4 +424,4 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main(sys.argv))
# Copyright (c) 2011 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# GNU Make based build file.  For details on GNU Make see:
#   http://www.gnu.org/software/make/manual/make.html
#
#
# Project information
#
# These variables store project specific settings for the project name
# build flags, files to copy or install.  In the examples it is typically
# only the list of sources and project name that will actually change and
# the rest of the makefile is boilerplate for defining build rules.
#
PROJECT:=dlopen
COPY_FILES:=dlopen.html
LDFLAGS:=-ldl -lppapi_cpp -lppapi
NEXES:=$(PROJECT)_x86_32.nexe $(PROJECT)_x86_64.nexe
NEXES+=lib32/libeightball.so lib64/libeightball.so
#
# Get pepper directory for toolchain and includes.
#
# If PEPPER_ROOT is not set, then assume it can be found a two directories up,
# from the default example directory location.
#
THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST)))
PEPPER_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..)
# Project Build flags
DEFINES:=
INCLUDES:=
WARNINGS:=-Wno-long-long -Wall -Wswitch-enum
CXXFLAGS:=-pthread $(WARNINGS) $(DEFINES) $(INCLUDES)
#
# Compute tool paths
#
#
OSNAME:=$(shell python $(PEPPER_ROOT)/tools/getos.py)
TC_PATH:=$(abspath $(PEPPER_ROOT)/toolchain/$(OSNAME)_x86_glibc)
CC:=$(TC_PATH)/bin/i686-nacl-gcc
CXX:=$(TC_PATH)/bin/i686-nacl-g++
STRIP:=$(TC_PATH)/bin/i686-nacl-strip
#
# Create shell aliases
#
# Create Python based aliases for common shell commands like copy or move.
#
COPY:= python $(PEPPER_ROOT)/tools/oshelpers.py cp
MKDIR:= python $(PEPPER_ROOT)/tools/oshelpers.py mkdir
RM:= python $(PEPPER_ROOT)/tools/oshelpers.py rm
MV:= python $(PEPPER_ROOT)/tools/oshelpers.py mv
#
# NMF Manifiest generation
#
NMF:=python $(PEPPER_ROOT)/tools/create_nmf.py
NMF+=-D $(TC_PATH)/x86_64-nacl/bin/objdump
NMF_PATHS:=-L $(TC_PATH)/x86_64-nacl/lib32 -L $(TC_PATH)/x86_64-nacl/lib
NMF_PATHS+=-L lib32 -L lib64
#
# Disable DOS PATH warning when using Cygwin based tools Windows
#
CYGWIN ?= nodosfilewarning
export CYGWIN
#
# Define a macro for copying files to the configuration directory
#
# Copys a source file to the destination directory, removing the base path
# from the source. Adds a dependency to the destination directory in case it
# needs to be created.
#
# $(1) = Source file
# $(2) = Destination directory
define FILE_COPY
$(2)/$(notdir $(1)) : $(1) | $(2)
$(COPY) $(1) $(2)
$(2)_COPIES+=$(2)/$(notdir $(1))
endef
# Declare the ALL target first, to make the 'all' target the default build
all: DEBUG RELEASE
#
# Debug Build rules.
#
DEBUG_x86_32_FLAGS:=-m32 -O0 -g
DEBUG_x86_64_FLAGS:=-m64 -O0 -g
# Create DBG configuration directories
DBG:
$(MKDIR) -p $@
DBG/lib32:
$(MKDIR) -p $@
DBG/lib64:
$(MKDIR) -p $@
# Copy all files to that config
$(foreach src,$(COPY_FILES),$(eval $(call FILE_COPY,$(src),DBG)))
# Build debug version dlopen nexe and eightball.so for 32 and 64 bit.
DBG/dlopen_x86_32.o: dlopen.cc $(THIS_MAKE) | DBG
$(CXX) -o $@ -c $< $(DEBUG_x86_32_FLAGS) $(CXXFLAGS)
DBG/dlopen_x86_32.nexe: DBG/dlopen_x86_32.o $(THIS_MAKE) | DBG
$(CXX) -o $@ $< $(DEBUG_x86_32_FLAGS) $(LDFLAGS)
DBG/dlopen_x86_64.o: dlopen.cc $(THIS_MAKE) | DBG
$(CXX) -o $@ -c $< $(DEBUG_x86_64_FLAGS) $(CXXFLAGS)
DBG/dlopen_x86_64.nexe: DBG/dlopen_x86_64.o $(THIS_MAKE) | DBG
$(CXX) -o $@ $< $(DEBUG_x86_64_FLAGS) $(LDFLAGS)
DBG/eightball_x86_32.o: eightball.cc $(THIS_MAKE) | DBG
$(CXX) -o $@ -c $< $(DEBUG_x86_32_FLAGS) $(CXXFLAGS) -fPIC
DBG/lib32/libeightball.so: DBG/eightball_x86_32.o $(THIS_MAKE) | DBG/lib32
$(CXX) -o $@ $< $(DEBUG_x86_32_FLAGS) $(LDFLAGS) -shared
DBG/eightball_x86_64.o: eightball.cc $(THIS_MAKE) | DBG
$(CXX) -o $@ -c $< $(DEBUG_x86_64_FLAGS) $(CXXFLAGS) -fPIC
DBG/lib64/libeightball.so: DBG/eightball_x86_64.o $(THIS_MAKE) | DBG/lib64
$(CXX) -o $@ $< $(DEBUG_x86_64_FLAGS) $(LDFLAGS) -shared
# Define rule for building NMF file and copying dependencies
DBG_NEXES:=$(foreach src,$(NEXES),DBG/$(src))
DBG/$(PROJECT).nmf : $(DBG_NEXES)
cd DBG && $(NMF) -o dlopen.nmf -s . $(NMF_PATHS) $(NEXES)
# Define a DEBUG alias to build the debug version
.PHONY : DEBUG RUN_DEBUG
DEBUG : $(DBG_NEXES) DBG/$(PROJECT).nmf $(DBG_COPIES)
# Define a RUN_DEBUG alias to build and server the DEBUG version
RUN_DEBUG: DEBUG
cd DBG && python ../../httpd.py
#
# Release build rules.
#
RELEASE_x86_32_FLAGS:=-m32 -O2
RELEASE_x86_64_FLAGS:=-m64 -O2
REL:
$(MKDIR) -p $@
REL/lib32:
$(MKDIR) -p $@
REL/lib64:
$(MKDIR) -p $@
# Copy all files to that config
$(foreach src,$(COPY_FILES),$(eval $(call FILE_COPY,$(src),REL)))
# Build release version dlopen nexe and eightball.so for 32 and 64 bit.
REL/dlopen_x86_32.o: dlopen.cc $(THIS_MAKE) | REL
$(CXX) -o $@ -c $< $(RELEASE_x86_32_FLAGS) $(CXXFLAGS)
REL/dlopen_x86_32.nexe: REL/dlopen_x86_32.o $(THIS_MAKE) | REL
$(CXX) -o $@ $< $(RELEASE_x86_32_FLAGS) $(LDFLAGS)
REL/dlopen_x86_64.o: dlopen.cc $(THIS_MAKE) | REL
$(CXX) -o $@ -c $< $(RELEASE_x86_64_FLAGS) $(CXXFLAGS)
REL/dlopen_x86_64.nexe: REL/dlopen_x86_64.o $(THIS_MAKE) | REL
$(CXX) -o $@ $< $(RELEASE_x86_64_FLAGS) $(LDFLAGS)
REL/eightball_x86_32.o: eightball.cc $(THIS_MAKE) | REL/lib32
$(CXX) -o $@ -c $< $(RELEASE_x86_32_FLAGS) $(CXXFLAGS) -fPIC
REL/lib32/libeightball.so: REL/eightball_x86_32.o $(THIS_MAKE) | REL/lib32
$(CXX) -o $@ $< $(RELEASE_x86_32_FLAGS) $(LDFLAGS) -shared
REL/eightball_x86_64.o: eightball.cc $(THIS_MAKE) | REL/lib64
$(CXX) -o $@ -c $< $(RELEASE_x86_64_FLAGS) $(CXXFLAGS) -fPIC
REL/lib64/libeightball.so: REL/eightball_x86_64.o $(THIS_MAKE) | REL/lib64
$(CXX) -o $@ $< $(RELEASE_x86_64_FLAGS) $(LDFLAGS) -shared
# Define rule for building NMF file and copying dependencies
REL_NEXES:=$(foreach src,$(NEXES),REL/$(src))
REL/$(PROJECT).nmf : $(REL_NEXES)
cd REL && $(NMF) -o dlopen.nmf -s . $(NMF_PATHS) $(NEXES)
# Define a RELEASE alias to build the release version
.PHONY : RELEASE RUN_RELEASE
RELEASE : $(REL_NEXES) REL/$(PROJECT).nmf $(REL_COPIES)
# Define a RUN_RELEASE alias to build and server the RELEASE version
RUN_RELEASE: RELEASE
cd REL && python ../../httpd.py
// Copyright (c) 2012 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// @file
/// This example demonstrates building a dynamic library which is loaded by the
/// NaCl module. To load the NaCl module, the browser first looks for the
/// CreateModule() factory method (at the end of this file). It calls
/// CreateModule() once to load the module code from your .nexe. After the
/// .nexe code is loaded, CreateModule() is not called again.
///
/// Once the .nexe code is loaded, the browser then calls the CreateInstance()
/// method on the object returned by CreateModule(). If the CreateInstance
/// returns successfully, then Init function is called, which will load the
/// shared object on a worker thread. We use a worker because dlopen is
/// a blocking call, which is not alowed on the main thread.
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <ppapi/cpp/module.h>
#include <ppapi/cpp/completion_callback.h>
#include <ppapi/cpp/var.h>
#include <ppapi/cpp/instance.h>
#include "eightball.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 occurrence of the <embed> tag that has these
/// attributes:
/// <pre>
/// type="application/x-nacl"
/// nacl="dlopen.nmf"
/// </pre>
class dlOpenInstance : public pp::Instance {
public:
explicit dlOpenInstance(pp::Core *core, PP_Instance instance):
pp::Instance(instance) {
_dlhandle = NULL;
_eightball = NULL;
_core = core;
_tid = 0;
};
virtual ~dlOpenInstance(){};
// Helper function to post a message back to the JS and stdout functions.
void logmsg(const char* pStr){
PostMessage(pp::Var(pStr));
fprintf(stdout, pStr);
}
// Initialize the module, staring a worker thread to load the shared object.
virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]){
logmsg("Spawning thread to cache .so files...");
if (pthread_create(&_tid, NULL, LoadLibrariesOnWorker, this)) {
logmsg("ERROR; pthread_create() failed.\n");
return false;
}
return true;
}
// This function is called on a worker thread, and will call dlopen to load
// the shared object. In addition, note that this function does NOT call
// dlclose, which would close the shared object and unload it from memory.
void LoadLibrary()
{
const int32_t IMMEDIATELY = 0;
_dlhandle = dlopen("libeightball.so", RTLD_LAZY);
pp::CompletionCallback cc(LoadDoneCB, this);
_core->CallOnMainThread(IMMEDIATELY, cc , 0);
}
// This function will run on the main thread and use the handle it stored by
// the worker thread, assuming it successfully loaded, to get a pointer to the
// message function in the shared object.
void UseLibrary() {
_dlhandle = dlopen("libeightball.so", RTLD_LAZY);
if(_dlhandle == NULL) {
logmsg("libeightball.so did not load");
} else {
_eightball = (TYPE_eightball) dlsym(this->_dlhandle, "Magic8Ball");
if (NULL == _eightball) {
std::string ballmessage = "dlsym() returned NULL: ";
ballmessage += dlerror();
ballmessage += "\n";
logmsg(ballmessage.c_str());
}
else{
logmsg("Eightball loaded!");
}
}
}
// Called by the browser to handle the postMessage() call in Javascript.
virtual void HandleMessage(const pp::Var& var_message) {
if(NULL == _eightball){
logmsg("Eightball library not loaded");
return;
}
if (!var_message.is_string()) {
logmsg("Message is not a string.");
return;
}
std::string message = var_message.AsString();
if (message == "query") {
fprintf(stdout, "%s(%d) Got this far.\n", __FILE__, __LINE__);
std::string ballmessage = "!The Magic 8-Ball says: ";
ballmessage += this->_eightball();
logmsg(ballmessage.c_str());
fprintf(stdout, "%s(%d) Got this far.\n", __FILE__, __LINE__);
} else {
std::string errormsg = "Unexpected message: ";
errormsg += message + "\n";
logmsg(errormsg.c_str());
}
}
static void* LoadLibrariesOnWorker(void *pInst) {
dlOpenInstance *inst = static_cast<dlOpenInstance *>(pInst);
inst->LoadLibrary();
return NULL;
}
static void LoadDoneCB(void *pInst, int32_t result) {
dlOpenInstance *inst = static_cast<dlOpenInstance *>(pInst);
inst->UseLibrary();
}
private:
void *_dlhandle;
TYPE_eightball _eightball;
pp::Core *_core;
pthread_t _tid;
};
// 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 dlOpenModule : public pp::Module {
public:
dlOpenModule() : pp::Module() {}
virtual ~dlOpenModule() {}
// Create and return a dlOpenInstance object.
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new dlOpenInstance(core(), instance);
}
};
// Factory function called by the browser when the module is first loaded.
// The browser keeps a singleton of this module. It calls the
// CreateInstance() method on the object you return to make instances. There
// is one instance per <embed> tag on the page. This is the main binding
// point for your NaCl module with the browser.
namespace pp {
Module* CreateModule() {
return new dlOpenModule();
}
} // namespace pp
<!DOCTYPE html>
<html>
<!--
Copyright (c) 2011 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head>
<title>Magic Eightball</title>
<script type="text/javascript">
function moduleDidLoad() {
}
function handleMessage(message_event) {
if(message_event.data=='Eightball loaded!')
{
document.getElementById('consolec').innerHTML = " \
Eightball loaded, type a question below, press the button, and get a response. \
<br /> \
<form name='form' Value='Hello Me' onSubmit='return askBall()'> \
<input type='textarea' size='64' name='inputtext' /> \
<input type='button' NAME='button' Value='ASK!' onClick='askBall()' /> \
</form>";
}
else
{
if(message_event.data[0]=='!')
{
document.getElementById('answerlog').innerHTML +=
(document.form.inputtext.value + ": " + message_event.data +"<br />");
}
else
{
document.getElementById('consolec').innerHTML +=
message_event.data + "<br />";
console.log(message_event.data);
}
}
}
function pageDidUnload() {
clearInterval(paintInterval);
}
function askBall()
{
dlopen.postMessage('query');
return false;
}
</script>
</head>
<body id="bodyId" onunload="pageDidUnload()">
<div id="listener">
<script type="text/javascript">
var listener = document.getElementById('listener')
listener.addEventListener('load', moduleDidLoad, true);
listener.addEventListener('message', handleMessage, true);
</script>
<h1>The Magic 8 Ball </h1>
<embed name="nacl_module"
id="dlopen"
width=1 height=1
src="dlopen.nmf"
type="application/x-nacl" />
</div>
<br />
<div id="consolec">..loading dynamic libraries...</div>
<div id="answerlog"></div>
</body>
</html>
#include <stdlib.h>
#include <stdio.h>
#include "eightball.h"
extern "C" const char *Magic8Ball() {
const int NSIDES = 8;
const char* answer[NSIDES] = {
"YES",
"NO",
"MAYBE",
"MAYBE NOT",
"DEFINITELY",
"ASK ME TOMORROW",
"PARTLY CLOUDY",
"42",
};
return answer[rand() % NSIDES];
}
#ifndef __EIGHTBALL_H__
#define __EIGHTBALL_H__
/* Return an answer. Question not required */
typedef char* (*TYPE_eightball)(void);
extern "C" const char* Magic8Ball();
#endif /* __EIGHTBALL_H__ */
# Copyright (c) 2012 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# GNU Make based build file.  For details on GNU Make see:
#   http://www.gnu.org/software/make/manual/make.html
#
#
# Project information
#
# These variables store project specific settings for the project name
# build flags, files to copy or install.  In the examples it is typically
# only the list of sources and project name that will actually change and
# the rest of the makefile is boilerplate for defining build rules.
#
PROJECT:=hello_world
CXX_SOURCES:=hello_world.cc helper_functions.cc
COPY_FILES:=hello_world.html
LDFLAGS:=-lppapi_cpp -lppapi
#
# Get pepper directory for toolchain and includes.
#
# If PEPPER_ROOT is not set, then assume it can be found a two directories up,
# from the default example directory location.
#
THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST)))
PEPPER_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..)
# Project Build flags
DEFINES:=
INCLUDES:=
WARNINGS:=-Wno-long-long -Wall -Wswitch-enum -pedantic -Werror
CXXFLAGS:= -shared -pthread -std=gnu++98 $(WARNINGS) $(DEFINES) $(INCLUDES)
#
# Compute tool paths
#
#
OSNAME:=$(shell python $(PEPPER_ROOT)/tools/getos.py)
TC_PATH:=$(abspath $(PEPPER_ROOT)/toolchain/$(OSNAME)_x86_glibc)
CC:=$(TC_PATH)/bin/i686-nacl-gcc
CXX:=$(TC_PATH)/bin/i686-nacl-g++
STRIP:=$(TC_PATH)/bin/i686-nacl-strip
#
# Create shell aliases
#
# Create Python based aliases for common shell commands like copy or move.
#
COPY:= python $(PEPPER_ROOT)/tools/oshelpers.py cp
MKDIR:= python $(PEPPER_ROOT)/tools/oshelpers.py mkdir
RM:= python $(PEPPER_ROOT)/tools/oshelpers.py rm
MV:= python $(PEPPER_ROOT)/tools/oshelpers.py mv
#
# NMF Manifiest generation
#
NMF:=python $(PEPPER_ROOT)/tools/create_nmf.py
NMF+=-D $(TC_PATH)/x86_64-nacl/bin/objdump
NMF_PATHS:=-L $(TC_PATH)/x86_64-nacl/lib32 -L $(TC_PATH)/x86_64-nacl/lib
#
# Disable DOS PATH warning when using Cygwin based tools Windows
#
CYGWIN ?= nodosfilewarning
export CYGWIN
#
# Define a macro for copying files to the configuration directory
#
# Copys a source file to the destination directory, removing the base path
# from the source. Adds a dependency to the destination directory in case it
# needs to be created.
#
# $(1) = Source file
# $(2) = Destination directory
define FILE_COPY
$(2)/$(notdir $(1)) : $(1) | $(2)
$(COPY) $(1) $(2)
$(2)_COPIES+=$(2)/$(notdir $(1))
endef
# Declare the ALL target first, to make the 'all' target the default build
all: DEBUG RELEASE
#
# Debug Build rules.
#
DEBUG_x86_32_FLAGS:=-m32 -O0 -g
DEBUG_x86_64_FLAGS:=-m64 -O0 -g
DEBUG_x86_32_OBJS:=$(patsubst %.cc,DBG/x86_32/%.o,$(CXX_SOURCES))
DEBUG_x86_64_OBJS:=$(patsubst %.cc,DBG/x86_64/%.o,$(CXX_SOURCES))
# Create DBG configuration directories
DBG:
$(MKDIR) -p $@
DBG/x86_32:
$(MKDIR) -p $@
DBG/x86_64:
$(MKDIR) -p $@
# Copy all files to that config
$(foreach src,$(COPY_FILES),$(eval $(call FILE_COPY,$(src),DBG)))
# Include generated dependencies
-include DBG/x86_32/*.d
-include DBG/x86_64/*.d
# Define compile rule for all 32 bit debug objects
DBG/x86_32/%.o : %.cc $(THIS_MAKE) | DBG/x86_32
$(CXX) -o $@ -c $< $(DEBUG_x86_32_FLAGS) $(CXXFLAGS) -MMD -MF $@.d
# Define compile rule for all 64 bit debug objects
DBG/x86_64/%.o : %.cc $(THIS_MAKE) | DBG/x86_64
$(CXX) -o $@ -c $< $(DEBUG_x86_64_FLAGS) $(CXXFLAGS) -MMD -MF $@.d
# Define Link rule for 32 bit debug NEXE
DBG/$(PROJECT)_x86_32.nexe : $(DEBUG_x86_32_OBJS)
$(CXX) -o $@ $^ $(DEBUG_x86_32_FLAGS) $(LDFLAGS)
# Define Link rule for 64 bit debug NEXE
DBG/$(PROJECT)_x86_64.nexe : $(DEBUG_x86_64_OBJS)
$(CXX) -o $@ $^ $(DEBUG_x86_64_FLAGS) $(LDFLAGS)
# Define rule for building NMF file and copying dependencies
DBG/$(PROJECT).nmf : DBG/$(PROJECT)_x86_64.nexe DBG/$(PROJECT)_x86_32.nexe
$(NMF) -o $@ -s DBG $(NMF_PATHS) $^
# Define a DEBUG alias to build the debug version
DBG_NEXES:= DBG/$(PROJECT)_x86_32.nexe DBG/$(PROJECT)_x86_64.nexe
.PHONY : DEBUG RUN_DEBUG
DEBUG : $(DBG_NEXES) DBG/$(PROJECT).nmf $(DBG_COPIES)
# Define a RUN_DEBUG alias to build and server the DEBUG version
RUN_DEBUG: DEBUG
cd DBG && python ../../httpd.py
#
# Release build rules.
#
RELEASE_x86_32_FLAGS:=-m32 -O2 -g
RELEASE_x86_64_FLAGS:=-m64 -O2 -g
RELEASE_x86_32_OBJS:=$(patsubst %.cc,REL/x86_32/%.o,$(CXX_SOURCES))
RELEASE_x86_64_OBJS:=$(patsubst %.cc,REL/x86_64/%.o,$(CXX_SOURCES))
REL:
$(MKDIR) -p $@
REL/x86_32:
$(MKDIR) -p $@
REL/x86_64:
$(MKDIR) -p $@
# Copy all files to that config
$(foreach src,$(COPY_FILES),$(eval $(call FILE_COPY,$(src),REL)))
# Include generated dependencies
-include REL/x86_32/*.d
-include REL/x86_64/*.d
# Define compile rule for all 32 bit debug objects
REL/x86_32/%.o : %.cc $(THIS_MAKE) | REL/x86_32
$(CXX) -o $@ -c $< $(RELEASE_x86_32_FLAGS) $(CXXFLAGS) -MMD -MF $@.d
# Define compile rule for all 64 bit debug objects
REL/x86_64/%.o : %.cc $(THIS_MAKE) | REL/x86_64
$(CXX) -o $@ -c $< $(RELEASE_x86_64_FLAGS) $(CXXFLAGS) -MMD -MF $@.d
# Define Link rule for 32 bit optimized and stripped NEXE
REL/$(PROJECT)_x86_32.nexe : $(RELEASE_x86_32_OBJS)
$(CXX) -o $@.unstripped $^ $(RELEASE_x86_32_FLAGS) $(LDFLAGS)
$(STRIP) $< -o $@
# Define Link rule for 64 bit optimized and stripped NEXE
REL/$(PROJECT)_x86_64.nexe : $(RELEASE_x86_64_OBJS)
$(CXX) -o $@.unstripped $^ $(RELEASE_x86_64_FLAGS) $(LDFLAGS)
$(STRIP) $@.unstripped -o $@
# Define rule for building NMF file and copying dependencies
REL/$(PROJECT).nmf : REL/$(PROJECT)_x86_64.nexe REL/$(PROJECT)_x86_32.nexe
$(NMF) -o $@ -s REL $(NMF_PATHS) $^
# Define a RELEASE alias to build the debug version
.PHONY : RELEASE RUN_RELEASE
REL_NEXES:=REL/$(PROJECT)_x86_32.nexe REL/$(PROJECT)_x86_64.nexe
RELEASE : $(REL_NEXES) REL/$(PROJECT).nmf $(REL_COPIES)
# Define a RUN_RELEASE alias to build and server the RELEASE version
RUN_RELEASE: RELEASE
cd REL && python ../../httpd.py
// 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.
/// @file
/// This example demonstrates loading, running and scripting a very simple NaCl
/// module. To load the NaCl module, the browser first looks for the
/// CreateModule() factory method (at the end of this file). It calls
/// CreateModule() once to load the module code from your .nexe. After the
/// .nexe code is loaded, CreateModule() is not called again.
///
/// Once the .nexe code is loaded, the browser then calls the
/// HelloWorldModule::CreateInstance()
/// method on the object returned by CreateModule(). It calls CreateInstance()
/// each time it encounters an <embed> tag that references your NaCl module.
#include <cstdio>
#include <cstring>
#include <string>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "helper_functions.h"
namespace hello_world {
/// Method name for ReverseText, as seen by JavaScript code.
const char* const kReverseTextMethodId = "reverseText";
/// Method name for FortyTwo, as seen by Javascript code. @see FortyTwo()
const char* const kFortyTwoMethodId = "fortyTwo";
/// Separator character for the reverseText method.
static const char kMessageArgumentSeparator = ':';
/// This is the module's function that invokes FortyTwo and converts the return
/// value from an int32_t to a pp::Var for return.
pp::Var MarshallFortyTwo() {
return pp::Var(FortyTwo());
}
/// This function is passed the arg list from the JavaScript call to
/// @a reverseText.
/// It makes sure that there is one argument and that it is a string, returning
/// an error message if it is not.
/// On good input, it calls ReverseText and returns the result. The result is
/// then sent back via a call to PostMessage.
pp::Var MarshallReverseText(const std::string& text) {
return pp::Var(ReverseText(text));
}
/// The Instance class. One of these exists for each instance of your NaCl
/// module on the web page. The browser will ask the Module object to create
/// a new Instance for each occurrence of the <embed> tag that has these
/// attributes:
/// <pre>
/// type="application/x-nacl"
/// nacl="hello_world.nmf"
/// </pre>
class HelloWorldInstance : public pp::Instance {
public:
explicit HelloWorldInstance(PP_Instance instance) : pp::Instance(instance) {
printf("HelloWorldInstance.\n");
}
virtual ~HelloWorldInstance() {}
/// Called by the browser to handle the postMessage() call in Javascript.
/// Detects which method is being called from the message contents, and
/// calls the appropriate function. Posts the result back to the browser
/// asynchronously.
/// @param[in] var_message The message posted by the browser. The possible
/// messages are 'fortyTwo' and 'reverseText:Hello World'. Note that
/// the 'reverseText' form contains the string to reverse following a ':'
/// separator.
virtual void HandleMessage(const pp::Var& var_message);
};
void HelloWorldInstance::HandleMessage(const pp::Var& var_message) {
if (!var_message.is_string()) {
return;
}
std::string message = var_message.AsString();
pp::Var return_var;
if (message == kFortyTwoMethodId) {
// Note that no arguments are passed in to FortyTwo.
return_var = MarshallFortyTwo();
} else if (message.find(kReverseTextMethodId) == 0) {
// The argument to reverseText is everything after the first ':'.
size_t sep_pos = message.find_first_of(kMessageArgumentSeparator);
if (sep_pos != std::string::npos) {
std::string string_arg = message.substr(sep_pos + 1);
return_var = MarshallReverseText(string_arg);
}
}
// Post the return result back to the browser. Note that HandleMessage() is
// always called on the main thread, so it's OK to post the return message
// directly from here. The return post is asynhronous: PostMessage returns
// immediately.
PostMessage(return_var);
}
/// The Module class. The browser calls the CreateInstance() method to create
/// an instance of your NaCl module on the web page. The browser creates a new
/// instance for each <embed> tag with
/// <code>type="application/x-nacl"</code>.
class HelloWorldModule : public pp::Module {
public:
HelloWorldModule() : pp::Module() {
printf("Got here.\n");
}
virtual ~HelloWorldModule() {}
/// Create and return a HelloWorldInstance object.
/// @param[in] instance a handle to a plug-in instance.
/// @return a newly created HelloWorldInstance.
/// @note The browser is responsible for calling @a delete when done.
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new HelloWorldInstance(instance);
}
};
} // namespace hello_world
namespace pp {
/// Factory function called by the browser when the module is first loaded.
/// The browser keeps a singleton of this module. It calls the
/// CreateInstance() method on the object you return to make instances. There
/// is one instance per <embed> tag on the page. This is the main binding
/// point for your NaCl module with the browser.
/// @return new HelloWorldModule.
/// @note The browser is responsible for deleting returned @a Module.
Module* CreateModule() {
return new hello_world::HelloWorldModule();
}
} // namespace pp
<!DOCTYPE html>
<html>
<!--
Copyright (c) 2011 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head>
<title>Hello, World!</title>
<script type="text/javascript">
helloWorldModule = null; // Global application object.
statusText = 'NO-STATUS';
// Indicate success when the NaCl module has loaded.
function moduleDidLoad() {
helloWorldModule = document.getElementById('hello_world');
updateStatus('SUCCESS');
}
// Handle a message coming from the NaCl module.
function handleMessage(message_event) {
alert(message_event.data);
}
// If the page loads before the Native Client module loads, then set the
// status message indicating that the module is still loading. Otherwise,
// do not change the status message.
function pageDidLoad() {
// Set the focus on the text input box. Doing this means you can press
// return as soon as the page loads, and it will fire the reversetText()
// function.
document.forms.helloForm.inputBox.focus();
if (helloWorldModule == null) {
updateStatus('LOADING...');
} else {
// It's possible that the Native Client module onload event fired
// before the page's onload event. In this case, the status message
// will reflect 'SUCCESS', but won't be displayed. This call will
// display the current message.
updateStatus();
}
}
function fortyTwo() {
helloWorldModule.postMessage('fortyTwo');
}
function reverseText() {
// Grab the text from the text box, pass it into reverseText()
var inputBox = document.forms.helloForm.inputBox;
helloWorldModule.postMessage('reverseText:' + inputBox.value);
// Note: a |false| return tells the <form> tag to cancel the GET action
// when submitting the form.
return false;
}
// Set the global status message. If the element with id 'statusField'
// exists, then set its HTML to the status message as well.
// opt_message The message test. If this is null or undefined, then
// attempt to set the element with id 'statusField' to the value of
// |statusText|.
function updateStatus(opt_message) {
if (opt_message)
statusText = opt_message;
var statusField = document.getElementById('statusField');
if (statusField) {
statusField.innerHTML = statusText;
}
}
</script>
</head>
<body onload="pageDidLoad()">
<h1>Native Client Simple Module</h1>
<p>
<form name="helloForm"
action=""
method="get"
onsubmit="return reverseText()">
<input type="text" id="inputBox" name="inputBox" value="Hello world" /><p/>
<input type="button" value="Call fortyTwo()" onclick="fortyTwo()" />
<input type="submit" value="Call reverseText()" />
</form>
<!-- Load the published .nexe. This includes the 'src' attribute which
shows how to load multi-architecture modules. Each entry in the "nexes"
object in the .nmf manifest file is a key-value pair: the key is the runtime
('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl module.
To load the debug versions of your .nexes, set the 'src' attribute to the
_dbg.nmf version of the manifest file.
Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
and a 'message' event listener attached. This wrapping method is used
instead of attaching the event listeners directly to the <EMBED> element to
ensure that the listeners are active before the NaCl module 'load' event
fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
pp::Instance.PostMessage() (in C++) from within the initialization code in
your NaCl module.
-->
<div id="listener">
<script type="text/javascript">
var listener = document.getElementById('listener')
listener.addEventListener('load', moduleDidLoad, true);
listener.addEventListener('message', handleMessage, true);
</script>
<embed name="nacl_module"
id="hello_world"
width=0 height=0
src="hello_world.nmf"
type="application/x-nacl" />
</div>
</p>
<p>If the module is working correctly, a click on the "Call fortyTwo()" button
should open a popup dialog containing <b>42</b> as its value.</p>
<p> Clicking on the "Call reverseText()" button
should open a popup dialog containing the textbox contents and its reverse
as its value.</p>
<h2>Status</h2>
<div id="statusField">NO-STATUS</div>
</body>
</html>
// 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 <algorithm>
#include "helper_functions.h"
namespace hello_world {
int32_t FortyTwo() {
return 42;
}
std::string ReverseText(const std::string& text) {
std::string reversed_string(text);
// Use reverse to reverse |reversed_string| in place.
std::reverse(reversed_string.begin(), reversed_string.end());
return reversed_string;
}
} // namespace hello_world
// 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 EXAMPLES_HELLO_WORLD_HELPER_FUNCTIONS_H_
#define EXAMPLES_HELLO_WORLD_HELPER_FUNCTIONS_H_
/// @file
/// These functions are stand-ins for your complicated computations which you
/// want to run in native code. We do two very simple things: return 42, and
/// reverse a string. But you can imagine putting more complicated things here
/// which might be difficult or slow to achieve in JavaScript, such as
/// cryptography, artificial intelligence, signal processing, physics modeling,
/// etc. See hello_world.cc for the code which is required for loading a NaCl
/// application and exposing methods to JavaScript.
#include <ppapi/c/pp_stdint.h>
#include <string>
namespace hello_world {
/// This is the module's function that does the work to compute the value 42.
int32_t FortyTwo();
/// This function is passed a string and returns a copy of the string with the
/// characters in reverse order.
/// @param[in] text The string to reverse.
/// @return A copy of @a text with the characters in reverse order.
std::string ReverseText(const std::string& text);
} // namespace hello_world
#endif // EXAMPLES_HELLO_WORLD_HELPER_FUNCTIONS_H_
...@@ -29,10 +29,13 @@ FORMAT_ARCH_MAP = { ...@@ -29,10 +29,13 @@ FORMAT_ARCH_MAP = {
# Names returned by x86_64-nacl-objdump: # Names returned by x86_64-nacl-objdump:
'elf64-nacl': 'x86-64', 'elf64-nacl': 'x86-64',
'elf32-nacl': 'x86-32', 'elf32-nacl': 'x86-32',
# TODO(mball): Add support for 'arm-32' and 'portable' architectures
# 'elf32-little': 'arm-32',
} }
ARCH_LOCATION = {
'x86-32': 'lib32',
'x86-64': 'lib64',
}
# These constants are used within nmf files. # These constants are used within nmf files.
RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader
MAIN_NEXE = 'main.nexe' # Name of entry point for execution MAIN_NEXE = 'main.nexe' # Name of entry point for execution
...@@ -110,14 +113,16 @@ class NmfUtils(object): ...@@ -110,14 +113,16 @@ class NmfUtils(object):
self.needed = None self.needed = None
self.lib_prefix = lib_prefix or [] self.lib_prefix = lib_prefix or []
def GleanFromObjdump(self, files): def GleanFromObjdump(self, files):
'''Get architecture and dependency information for given files '''Get architecture and dependency information for given files
Args: Args:
files: A dict with key=filename and value=list or set of archs. E.g.: files: A dict with key=filename and value=list or set of archs. E.g.:
{ '/path/to/my.nexe': ['x86-32', 'x86-64'], { '/path/to/my.nexe': ['x86-32']
'/path/to/libmy.so': ['x86-32'], '/path/to/lib64/libmy.so': ['x86-64'],
'/path/to/my2.nexe': None } # Indicates all architectures '/path/to/mydata.so': ['x86-32', 'x86-64'],
'/path/to/my.data': None } # Indicates all architectures
Returns: A tuple with the following members: Returns: A tuple with the following members:
input_info: A dict with key=filename and value=ArchFile of input files. input_info: A dict with key=filename and value=ArchFile of input files.
...@@ -147,7 +152,7 @@ class NmfUtils(object): ...@@ -147,7 +152,7 @@ class NmfUtils(object):
arch=arch, arch=arch,
name=name, name=name,
path=filename, path=filename,
url='/'.join(self.lib_prefix + [arch, name])) url='/'.join(self.lib_prefix + [ARCH_LOCATION[arch], name]))
matched = NeededMatcher.match(line) matched = NeededMatcher.match(line)
if matched is not None: if matched is not None:
if files[filename] is None or arch in files[filename]: if files[filename] is None or arch in files[filename]:
...@@ -189,7 +194,7 @@ class NmfUtils(object): ...@@ -189,7 +194,7 @@ class NmfUtils(object):
all_files, unexamined = self.GleanFromObjdump( all_files, unexamined = self.GleanFromObjdump(
dict([(file, None) for file in self.main_files])) dict([(file, None) for file in self.main_files]))
for name, arch_file in all_files.items(): for name, arch_file in all_files.items():
arch_file.url = os.path.basename(name) arch_file.url = name
if unexamined: if unexamined:
unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD])) unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD]))
while unexamined: while unexamined:
...@@ -232,41 +237,43 @@ class NmfUtils(object): ...@@ -232,41 +237,43 @@ class NmfUtils(object):
os.path.normcase(os.path.abspath(destination))): os.path.normcase(os.path.abspath(destination))):
shutil.copy2(source, destination) shutil.copy2(source, destination)
def _GenerateManifest(self): def _GenerateManifest(self, runnable=True):
programs = {} '''Create a JSON formatted dict containing the files
files = {}
NaCl will map url requests based on architecture. The startup NEXE
can always be found under the top key PROGRAM. Additional files are under
the FILES key further mapped by file name. In the case of 'runnable' the
PROGRAM key is populated with urls pointing the runnable-ld.so which acts
as the startup nexe. The application itself, is then placed under the
FILES key mapped as 'main.exe' instead of it's original name so that the
loader can find it.'''
manifest = { FILES_KEY: {}, PROGRAM_KEY: {} }
needed = self.GetNeeded()
def add_files(needed): for need in needed:
for filename, arch_file in needed.items(): archinfo = needed[need]
files.setdefault(arch_file.arch, set()).add(arch_file.name) urlinfo = { URL_KEY: archinfo.url }
name = archinfo.name
# If starting with runnable-ld.so, make that the main executable.
if runnable:
if need.endswith(RUNNABLE_LD):
manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
continue
# For the main nexes:
if need.endswith('.nexe') and need in self.main_files:
# Place it under program if we aren't using the runnable-ld.so.
if not runnable:
manifest[PROGRAM_KEY][archinfo.arch] = urlinfo
continue
# Otherwise, treat it like another another file named main.nexe.
name = MAIN_NEXE
fileinfo = manifest[FILES_KEY].get(name, {})
fileinfo[archinfo.arch] = urlinfo
manifest[FILES_KEY][name] = fileinfo
needed = self.GetNeeded()
add_files(needed)
for filename in self.main_files:
arch_file = needed[filename]
programs[arch_file.arch] = arch_file.name
filemap = {}
for arch in files:
for file in files[arch]:
if file not in programs.values() and file != RUNNABLE_LD:
filemap.setdefault(file, set()).add(arch)
def arch_name(arch, file):
# nmf files expect unix-style path separators
return {URL_KEY: '/'.join(self.lib_prefix + [arch, file])}
# TODO(mcgrathr): perhaps notice a program with no deps
# (i.e. statically linked) and generate program=nexe instead?
manifest = {PROGRAM_KEY: {}, FILES_KEY: {MAIN_NEXE: {}}}
for arch in programs:
manifest[PROGRAM_KEY][arch] = arch_name(arch, RUNNABLE_LD)
manifest[FILES_KEY][MAIN_NEXE][arch] = {URL_KEY: programs[arch]}
for file in filemap:
manifest[FILES_KEY][file] = dict([(arch, arch_name(arch, file))
for arch in filemap[file]])
self.manifest = manifest self.manifest = manifest
def GetManifest(self): def GetManifest(self):
...@@ -301,6 +308,9 @@ def Main(argv): ...@@ -301,6 +308,9 @@ def Main(argv):
parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies',
help='Destination directory for staging libraries', help='Destination directory for staging libraries',
metavar='DIRECTORY') metavar='DIRECTORY')
parser.add_option('-r', '--remove', dest='remove',
help='Remove the prefix from the files.',
metavar='PATH')
(options, args) = parser.parse_args(argv) (options, args) = parser.parse_args(argv)
if len(args) < 1: if len(args) < 1:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment