diff options
author | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-01 22:34:58 +0000 |
---|---|---|
committer | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-01 22:34:58 +0000 |
commit | 0e151bf97ba5c90fdbf937722e0b4f97aeb5e99b (patch) | |
tree | 2ec337c5ffad394e5f87887b97bacebd18708651 /native_client_sdk | |
parent | e4420b5e3668468358a80fbca729dd75161c0e24 (diff) | |
download | chromium_src-0e151bf97ba5c90fdbf937722e0b4f97aeb5e99b.zip chromium_src-0e151bf97ba5c90fdbf937722e0b4f97aeb5e99b.tar.gz chromium_src-0e151bf97ba5c90fdbf937722e0b4f97aeb5e99b.tar.bz2 |
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
Diffstat (limited to 'native_client_sdk')
14 files changed, 1085 insertions, 53 deletions
diff --git a/native_client_sdk/src/build_tools/buildbot_run.py b/native_client_sdk/src/build_tools/buildbot_run.py index 54fd3d0..40f6e2d 100755 --- a/native_client_sdk/src/build_tools/buildbot_run.py +++ b/native_client_sdk/src/build_tools/buildbot_run.py @@ -16,6 +16,7 @@ and whether it should upload an SDK to file storage (GSTORE) """ # std python includes +import optparse import os import subprocess import sys @@ -24,7 +25,6 @@ import sys import build_utils import lastchange - # Create the various paths of interest SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) SDK_SRC_DIR = os.path.dirname(SCRIPT_DIR) @@ -116,21 +116,25 @@ def CopyDir(src, dst, excludes=['.svn']): print "cp -r %s %s" % (src, dst) oshelpers.Copy(args) + def RemoveDir(dst): """Remove the provided path.""" print "rm -fr " + dst oshelpers.Remove(['-fr', dst]) + def MakeDir(dst): """Create the path including all parent directories as needed.""" print "mkdir -p " + dst oshelpers.Mkdir(['-p', dst]) + def MoveDir(src, dst): """Move the path src to dst.""" print "mv -fr %s %s" % (src, dst) oshelpers.Move(['-f', src, dst]) + def BuildOutputDir(*paths): return os.path.join(OUT_DIR, *paths) @@ -190,6 +194,7 @@ def GetBuildArgs(tcname, tcpath, arch, xarch=None): args.append('--nacl_glibc') return args + header_map = { 'newlib': { 'pthread.h': 'src/untrusted/pthread/pthread.h', @@ -278,17 +283,39 @@ def InstallHeaders(tc_dst_inc, pepper_ver, tc_name): 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() arch = 'x86' - # the vars below are intended for debugging - skip_untar = 0 - skip_build = 0 - skip_tar = 0 - skip_examples = 0 - skip_headers = 0 - skip_make = 0 - force_archive = 0 + + skip_examples = False + skip_untar = False + skip_build = False + skip_headers = False + skip_tar = False + force_archive = options.archive + + 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() clnumber = lastchange.FetchVersionInfo(None).revision @@ -369,7 +396,6 @@ def main(): RemoveDir(os.path.join(pepperdir, 'examples')) CopyDir(os.path.join(SDK_SRC_DIR, 'examples'), pepperdir) - tarname = 'naclsdk_' + platform + '.bz2' BuildStep('Tar Pepper Bundle') if not skip_tar: @@ -382,7 +408,7 @@ def main(): BuildStep('Archive build') Archive(tarname) - if not skip_make: + if not skip_examples: BuildStep('Test Build Examples') filelist = os.listdir(os.path.join(pepperdir, 'examples')) for filenode in filelist: @@ -398,4 +424,4 @@ def main(): if __name__ == '__main__': - sys.exit(main()) + sys.exit(main(sys.argv)) diff --git a/native_client_sdk/src/examples/dlopen/Makefile b/native_client_sdk/src/examples/dlopen/Makefile new file mode 100644 index 0000000..adf8a78 --- /dev/null +++ b/native_client_sdk/src/examples/dlopen/Makefile @@ -0,0 +1,209 @@ +# 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 diff --git a/native_client_sdk/src/examples/dlopen/dlopen.cc b/native_client_sdk/src/examples/dlopen/dlopen.cc new file mode 100644 index 0000000..27f7c7c --- /dev/null +++ b/native_client_sdk/src/examples/dlopen/dlopen.cc @@ -0,0 +1,168 @@ +// 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 + diff --git a/native_client_sdk/src/examples/dlopen/dlopen.html b/native_client_sdk/src/examples/dlopen/dlopen.html new file mode 100644 index 0000000..d964c0e --- /dev/null +++ b/native_client_sdk/src/examples/dlopen/dlopen.html @@ -0,0 +1,71 @@ +<!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> diff --git a/native_client_sdk/src/examples/dlopen/eightball.cc b/native_client_sdk/src/examples/dlopen/eightball.cc new file mode 100644 index 0000000..df2be2a --- /dev/null +++ b/native_client_sdk/src/examples/dlopen/eightball.cc @@ -0,0 +1,20 @@ +#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]; +} + diff --git a/native_client_sdk/src/examples/dlopen/eightball.h b/native_client_sdk/src/examples/dlopen/eightball.h new file mode 100644 index 0000000..d0924d3 --- /dev/null +++ b/native_client_sdk/src/examples/dlopen/eightball.h @@ -0,0 +1,9 @@ +#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__ */ + diff --git a/native_client_sdk/src/examples/dlopen/make.bat b/native_client_sdk/src/examples/dlopen/make.bat new file mode 100755 index 0000000..479d648 --- /dev/null +++ b/native_client_sdk/src/examples/dlopen/make.bat @@ -0,0 +1 @@ +@..\..\tools\make.exe %* diff --git a/native_client_sdk/src/examples/hello_world_glibc/Makefile b/native_client_sdk/src/examples/hello_world_glibc/Makefile new file mode 100644 index 0000000..cf14374 --- /dev/null +++ b/native_client_sdk/src/examples/hello_world_glibc/Makefile @@ -0,0 +1,200 @@ +# 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 diff --git a/native_client_sdk/src/examples/hello_world_glibc/hello_world.cc b/native_client_sdk/src/examples/hello_world_glibc/hello_world.cc new file mode 100644 index 0000000..438b7bf --- /dev/null +++ b/native_client_sdk/src/examples/hello_world_glibc/hello_world.cc @@ -0,0 +1,135 @@ +// 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 diff --git a/native_client_sdk/src/examples/hello_world_glibc/hello_world.html b/native_client_sdk/src/examples/hello_world_glibc/hello_world.html new file mode 100644 index 0000000..ec1b4e7 --- /dev/null +++ b/native_client_sdk/src/examples/hello_world_glibc/hello_world.html @@ -0,0 +1,126 @@ +<!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> diff --git a/native_client_sdk/src/examples/hello_world_glibc/helper_functions.cc b/native_client_sdk/src/examples/hello_world_glibc/helper_functions.cc new file mode 100644 index 0000000..2b43c605 --- /dev/null +++ b/native_client_sdk/src/examples/hello_world_glibc/helper_functions.cc @@ -0,0 +1,22 @@ +// 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 + diff --git a/native_client_sdk/src/examples/hello_world_glibc/helper_functions.h b/native_client_sdk/src/examples/hello_world_glibc/helper_functions.h new file mode 100644 index 0000000..5076300 --- /dev/null +++ b/native_client_sdk/src/examples/hello_world_glibc/helper_functions.h @@ -0,0 +1,34 @@ +// 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_ + diff --git a/native_client_sdk/src/examples/hello_world_glibc/make.bat b/native_client_sdk/src/examples/hello_world_glibc/make.bat new file mode 100755 index 0000000..479d648 --- /dev/null +++ b/native_client_sdk/src/examples/hello_world_glibc/make.bat @@ -0,0 +1 @@ +@..\..\tools\make.exe %* diff --git a/native_client_sdk/src/tools/create_nmf.py b/native_client_sdk/src/tools/create_nmf.py index 7561bce..3c33b2a 100755 --- a/native_client_sdk/src/tools/create_nmf.py +++ b/native_client_sdk/src/tools/create_nmf.py @@ -29,10 +29,13 @@ FORMAT_ARCH_MAP = { # Names returned by x86_64-nacl-objdump: 'elf64-nacl': 'x86-64', 'elf32-nacl': 'x86-32', - # TODO(mball): Add support for 'arm-32' and 'portable' architectures - # 'elf32-little': 'arm-32', } +ARCH_LOCATION = { + 'x86-32': 'lib32', + 'x86-64': 'lib64', +} + # These constants are used within nmf files. RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader MAIN_NEXE = 'main.nexe' # Name of entry point for execution @@ -110,14 +113,16 @@ class NmfUtils(object): self.needed = None self.lib_prefix = lib_prefix or [] + def GleanFromObjdump(self, files): '''Get architecture and dependency information for given files Args: files: A dict with key=filename and value=list or set of archs. E.g.: - { '/path/to/my.nexe': ['x86-32', 'x86-64'], - '/path/to/libmy.so': ['x86-32'], - '/path/to/my2.nexe': None } # Indicates all architectures + { '/path/to/my.nexe': ['x86-32'] + '/path/to/lib64/libmy.so': ['x86-64'], + '/path/to/mydata.so': ['x86-32', 'x86-64'], + '/path/to/my.data': None } # Indicates all architectures Returns: A tuple with the following members: input_info: A dict with key=filename and value=ArchFile of input files. @@ -147,7 +152,7 @@ class NmfUtils(object): arch=arch, name=name, path=filename, - url='/'.join(self.lib_prefix + [arch, name])) + url='/'.join(self.lib_prefix + [ARCH_LOCATION[arch], name])) matched = NeededMatcher.match(line) if matched is not None: if files[filename] is None or arch in files[filename]: @@ -189,7 +194,7 @@ class NmfUtils(object): all_files, unexamined = self.GleanFromObjdump( dict([(file, None) for file in self.main_files])) for name, arch_file in all_files.items(): - arch_file.url = os.path.basename(name) + arch_file.url = name if unexamined: unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD])) while unexamined: @@ -232,41 +237,43 @@ class NmfUtils(object): os.path.normcase(os.path.abspath(destination))): shutil.copy2(source, destination) - def _GenerateManifest(self): - programs = {} - files = {} + def _GenerateManifest(self, runnable=True): + '''Create a JSON formatted dict containing the 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 filename, arch_file in needed.items(): - files.setdefault(arch_file.arch, set()).add(arch_file.name) + for need in needed: + archinfo = needed[need] + 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 def GetManifest(self): @@ -301,6 +308,9 @@ def Main(argv): parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies', help='Destination directory for staging libraries', metavar='DIRECTORY') + parser.add_option('-r', '--remove', dest='remove', + help='Remove the prefix from the files.', + metavar='PATH') (options, args) = parser.parse_args(argv) if len(args) < 1: |