aboutsummaryrefslogtreecommitdiffstats
path: root/src/bin/efl_js
diff options
context:
space:
mode:
authorFelipe Magno de Almeida <felipe@expertisesolutions.com.br>2014-09-01 15:08:49 -0300
committerFelipe Magno de Almeida <felipe@expertisesolutions.com.br>2015-12-19 17:05:28 -0200
commit844228b4060c77a41db6232103d8a5a20cd2bf95 (patch)
tree50aa3427c700318e3932b92702aac91a3dec586d /src/bin/efl_js
parentEvas textblock: Fix some indentation and formatting. (diff)
downloadefl-844228b4060c77a41db6232103d8a5a20cd2bf95.tar.gz
efl-js: JavaScript Eolian bindingdevs/felipealmeida/js-20151219
To configure efl sources with bindings to use in nodejs add ––with-js=nodejs in configure flags to generate node files $ configure --with-js=nodejs and compile normally with: $ make $ make install To use, you have to require efl: efl = require('efl') The bindings is divided in two parts: generated and manually written. The generation uses the Eolian library for parsing Eo files and generate C++ code that is compiled against V8 interpreter library to create a efl.node file that can be required in a node.js instance. @feature
Diffstat (limited to 'src/bin/efl_js')
-rwxr-xr-xsrc/bin/efl_js/efljslaunch139
-rw-r--r--src/bin/efl_js/efljslaunch.desktop7
-rw-r--r--src/bin/efl_js/efljslaunch.xml7
-rwxr-xr-xsrc/bin/efl_js/efljspack251
-rw-r--r--src/bin/efl_js/launcher_main.cc156
5 files changed, 560 insertions, 0 deletions
diff --git a/src/bin/efl_js/efljslaunch b/src/bin/efl_js/efljslaunch
new file mode 100755
index 0000000000..785c30e3e3
--- /dev/null
+++ b/src/bin/efl_js/efljslaunch
@@ -0,0 +1,139 @@
+#!/bin/sh
+':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"
+
+// Core node modules
+var path = require('path');
+var os = require('os');
+var zlib = require('zlib');
+var child_process = require('child_process');
+
+// 3rd party modules
+var fs = require('fs-extra');
+var getopt = require('node-getopt');
+var tar = require('tar');
+
+function make_error_cb(message)
+{
+ return function(e) {
+ console.error("Error %s: %s", message, e);
+ process.exit(1);
+ };
+}
+
+function remove_files(options)
+{
+ if (options.verbose)
+ console.log("Removing temporary files");
+
+ fs.remove(options.project_folder);
+}
+
+function run_project(options)
+{
+ if (options.verbose)
+ console.log("Running the project");
+
+ var current_dir = process.cwd();
+ process.chdir(options.project_root);
+
+ var proc = child_process.fork(options.metadata.Entry);
+ proc.on('exit', function(code){
+ if (options.verbose)
+ console.log('Child exited with code %s', code);
+ process.chdir(current_dir);
+ if (!options.keep)
+ remove_files(options);
+ });
+
+}
+
+function unpack_project_data(options)
+{
+ if (options.verbose)
+ console.log("Unpacking project sources and assets");
+
+ var datafile = path.join(options.project_folder, "data.tar.gz");
+ var project_root = path.join(options.project_folder, "root");
+
+ options.project_root = project_root;
+
+ var input = fs.createReadStream(datafile);
+ var unzipper = zlib.createGunzip();
+ var extractor = tar.Extract({path: project_root, strip: 0});
+
+ input.on('error', make_error_cb("reading package data file."));
+ extractor.on('error', make_error_cb("unpacking package data file."));
+ if (!("only-extract" in options))
+ extractor.on('end', function(){ run_project(options); });
+
+ input.pipe(unzipper)
+ unzipper.pipe(extractor);
+}
+
+function read_metadata(options)
+{
+ if (options.verbose)
+ console.log("Reading project metadata");
+
+ var project_folder = options.project_folder;
+ var metadata = JSON.parse(fs.readFileSync(path.join(project_folder, "meta.json")));
+
+ if (options.verbose)
+ console.log("Project: %s\nVersion: %s\nEntry point: %s", metadata.Name, metadata.Version, metadata.Entry);
+ if ("only-dump" in options)
+ process.exit(0);
+
+ options.metadata = metadata;
+
+ unpack_project_data(options);
+}
+
+function extract(filename, options)
+{
+ if (options.verbose)
+ console.log("Extracting ", filename, "with options ", options);
+
+ var project_id = path.basename(filename, ".epk");
+ var project_folder = path.join(options['temp-dir'], project_id);
+
+ options.project_folder = project_folder;
+ options.project_id = project_id;
+
+ var input = fs.createReadStream(filename);
+ var extractor = tar.Extract({path: options['temp-dir'], strip: 0});
+
+ input.on('error', make_error_cb("reading package file."));
+ extractor.on('error', make_error_cb("unpacking package file."));
+ extractor.on('end', function(){ read_metadata(options); });
+
+ input.pipe(extractor);
+}
+
+function main() {
+ var options = getopt.create([
+ ['d', 'only-dump', 'Only dump information about the package'],
+ ['e', 'only-extract', 'Only extract the package, do not run'],
+ ['h', 'help', 'Display this help'],
+ ['k', 'keep', 'Do not remove the files after exiting'],
+ ['t', 'temp-dir=ARG', 'Temporary dir to extract files'],
+ ['v', 'verbose', 'Print information messages'],
+ ]).bindHelp().parseSystem();
+
+ var filename = options.argv[0];
+ if (filename === undefined)
+ {
+ console.error("Must provide a package file.");
+ process.exit(1);
+ }
+
+ if (!('temp-dir' in options.options))
+ {
+ options.options["temp-dir"] = path.join(os.tmpdir(), "efljs_apps");
+ if (options.verbose)
+ console.log("Defaulting temp dir to ", options.options["temp-dir"]);
+ }
+
+ extract(filename, options.options);
+}
+
+main();
diff --git a/src/bin/efl_js/efljslaunch.desktop b/src/bin/efl_js/efljslaunch.desktop
new file mode 100644
index 0000000000..53371cba97
--- /dev/null
+++ b/src/bin/efl_js/efljslaunch.desktop
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Name=EFL JS package launcher
+Exec=efljslaunch %f
+Type=Application
+Categories=EFL
+Terminal=true
+MimeType=application/x-efljspackage;
diff --git a/src/bin/efl_js/efljslaunch.xml b/src/bin/efl_js/efljslaunch.xml
new file mode 100644
index 0000000000..b1db6841b2
--- /dev/null
+++ b/src/bin/efl_js/efljslaunch.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+ <mime-type type="application/x-efljspackage">
+ <comment xml:lang="en">EFL JS package</comment>
+ <glob pattern="*.epk"/>
+ </mime-type>
+</mime-info> \ No newline at end of file
diff --git a/src/bin/efl_js/efljspack b/src/bin/efl_js/efljspack
new file mode 100755
index 0000000000..50e27b6ac4
--- /dev/null
+++ b/src/bin/efl_js/efljspack
@@ -0,0 +1,251 @@
+#!/bin/sh
+':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"
+
+var zlib = require('zlib');
+var path = require('path');
+
+// external dependencies
+var fs = require('fs-extra');
+var tar = require('tar');
+var fstream = require('fstream');
+var getopt = require('node-getopt');
+
+/*
+ * Packing a project.
+ * The efljs package has a similar format to debian packages. It is a
+ * tar package containing two files:
+ *
+ * meta.txt: Metadata information about this package.
+ * data.tar.gz: Gzipped data, with the project tree ready to be decompressed
+ * and run by the package launcher.
+ *
+ * During the build, a out/ directory is created in the project root to
+ * store the package and temporary files.
+ */
+
+// Creates a stub .project file and packs it.
+function pack_single(sourcepath, options)
+{
+ if (options.verbose)
+ console.log("Creating project file for single file app", sourcepath);
+
+ var dir_name = path.dirname(fs.realpathSync(sourcepath));
+ var filename = path.basename(sourcepath);
+ var projectRegex = /^(.*).js$/g;
+ var project_name = projectRegex.exec(filename)[1];
+
+ if (!validade_project_name(project_name))
+ {
+ console.error("Invalid project name. Must start with a letter.");
+ process.exit(0);
+ }
+
+ var project_filename = path.join(dir_name, project_name + ".project");
+
+ var fd = fs.openSync(project_filename, 'w');
+
+ var jsonData = {};
+
+ jsonData["Name"] = project_name;
+ jsonData["Entry"] = filename;
+ jsonData["Sources"] = [[filename, '.']];
+ jsonData["Version"] = "0.1";
+
+ fs.writeSync(fd, JSON.stringify(jsonData, null, 2));
+
+ fs.closeSync(fd);
+
+ pack_project(project_filename, options);
+
+}
+
+function generate_build_info(configuration, project_file, options)
+{
+ build_info = {};
+
+ // project == project_dir
+ // /out == build_dir
+ // /data == data_dir
+ // /name-version == package_dir
+
+ build_info.package_id = configuration.Name + "-" + configuration.Version;
+ build_info.project_dir = path.dirname(project_file);
+ build_info.build_dir = path.join(build_info.project_dir, "out");
+ build_info.data_dir = path.join(build_info.build_dir, "data");
+ build_info.package_dir = path.join(build_info.build_dir, build_info.package_id);
+ build_info.data_file = path.join(build_info.package_dir, "data.tar.gz");
+ build_info.package_file = path.join(build_info.build_dir, build_info.package_id + ".epk")
+ build_info.metadata_file = path.join(build_info.package_dir, "meta.json");
+
+ if (options.verbose)
+ {
+ console.log("Project id: ", build_info.package_id);
+ console.log("Project source dir: ", build_info.project_dir);
+ console.log("Project build dir: ", build_info.build_dir);
+ console.log("Project data dir:", build_info.data_dir);
+ console.log("Project package dir:", build_info.package_dir);
+ }
+
+ return build_info;
+
+}
+
+// Project names must start with a letter and contain only
+// letters, digits and underscores.
+function validade_project_name(name)
+{
+ return (/^[a-zA-Z][\w-]*$/).test(name)
+}
+
+function pack_project(project_file, options)
+{
+ if (options.verbose)
+ console.log("Packing project from project file ", project_file);
+
+ var configuration = JSON.parse(fs.readFileSync(project_file));
+
+ if (!validade_project_name(configuration.Name))
+ {
+ console.error("Invalid project name. Must start with a letter.");
+ process.exit(0);
+ }
+
+ var build_info = generate_build_info(configuration, project_file, options);
+
+ try
+ {
+ fs.mkdirSync(build_info.build_dir);
+ fs.mkdirSync(build_info.data_dir);
+ fs.mkdirSync(build_info.package_dir);
+ }
+ catch (e)
+ {
+ console.warn("Warning: Project output directories not empty.");
+ }
+
+ create_metadata_file(configuration, build_info, options);
+
+ // If not explicitly named on configuration, add the entire directory
+ if (!('Sources' in configuration))
+ {
+ generate_source_list(configuration, build_info.project_dir, options);
+ }
+
+ create_project_tree(configuration.Sources, build_info, options);
+
+ pack_data_dir(build_info, options);
+}
+
+function create_project_tree(sources, build_info, options)
+{
+ for (var i = sources.length - 1; i >= 0; i--) {
+ if (options.verbose)
+ console.log("Adding file ", sources[i], "to package.");
+ var source_file = path.join(build_info.project_dir, sources[i][0]);
+ var destination_dir = path.join(build_info.data_dir, sources[i][1]);
+ var destination_filename = path.basename(source_file);
+ var destination_file = path.join(destination_dir, destination_filename);
+
+ fs.copySync(source_file, destination_file);
+ };
+}
+
+function generate_source_list(configuration, project_dir, options)
+{
+ console.log("Generating source list for project dir", build_info.project_dir);
+ var dir_entries = fs.readdirSync(project_dir);
+ var sources = [];
+
+ dir_entries.forEach(function(entry){
+ if (entry == "out")
+ return;
+ sources.push([entry, "."]);
+ });
+ configuration.Sources = sources;
+}
+
+function create_metadata_file(configuration, build_info, options)
+{
+ if (options.verbose)
+ console.log("Creating metadata file", build_info.metadata_file);
+
+ var metadata = {};
+
+ metadata.Name = configuration.Name;
+ metadata.Entry = configuration.Entry;
+ metadata.Version = configuration.Version;
+
+ var output = fs.createWriteStream(build_info.metadata_file);
+ output.write(JSON.stringify(metadata, null, 2));
+ output.close();
+}
+
+function pack_data_dir(build_info, options)
+{
+ if (options.verbose)
+ console.log("Packing data...");
+
+ pack_directory(build_info.data_dir, build_info.data_file, true, true, function(){
+ if (options.verbose)
+ console.log("Packed data");
+ pack_final_package(build_info, options);
+ });
+}
+
+function pack_final_package(build_info, options)
+{
+ if (options.verbose)
+ console.log("Creating package ", build_info.package_file);
+ pack_directory(build_info.package_dir, build_info.package_file, false, false, function(){
+ if (options.verbose)
+ console.log("Created project package.");
+ });
+}
+
+function pack_directory(source_dir, target_file, strip_base_dir, should_gzip, callback)
+{
+ var output = fs.createWriteStream(target_file);
+ var packer = tar.Pack({fromBase: strip_base_dir == true});
+ if (callback != undefined)
+ output.on('close', callback);
+
+ var reader = fstream.Reader({path: source_dir, type: "Directory"});
+ var destStr = reader.pipe(packer);
+ if(should_gzip)
+ destStr = destStr.pipe(zlib.createGzip());
+ destStr.pipe(output);
+}
+
+function main()
+{
+
+ var options = getopt.create([
+ ['v', 'verbose', 'Explain what is being done'],
+ ['h', 'help', 'Display this help']
+ ]).bindHelp().parseSystem();
+
+ filename = options.argv[0];
+
+ if (typeof filename === 'undefined')
+ {
+ console.error('Must provide a valid js or project file.');
+ process.exit(1);
+ }
+
+ if (endsWith(filename, ".js"))
+ {
+ pack_single(filename, options.options);
+ }
+ else if (endsWith(filename, ".project"))
+ {
+ pack_project(filename, options.options);
+ }
+}
+
+main();
+
+//// Helper functions
+function endsWith(str, suffix)
+{
+ return str.indexOf(suffix, str.length - suffix.length) !== -1;
+}
diff --git a/src/bin/efl_js/launcher_main.cc b/src/bin/efl_js/launcher_main.cc
new file mode 100644
index 0000000000..680f16ca52
--- /dev/null
+++ b/src/bin/efl_js/launcher_main.cc
@@ -0,0 +1,156 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <cerrno>
+
+#include <Eo_Js.hh>
+#include <Eina.hh>
+#include <Eo.hh>
+// #include <efl_js.hh>
+
+using namespace std;
+using namespace v8;
+
+const char PATH_SEPARATOR =
+#ifdef _WIN32
+ '\\';
+#else
+ '/';
+#endif
+
+static std::string get_file_contents(const char *filename) {
+ std::ifstream in(filename, std::ios::in);
+ if (in) {
+ std::ostringstream contents;
+ contents << in.rdbuf();
+ in.close();
+ return contents.str();
+ } else {
+ throw(errno);
+ }
+}
+
+static std::string get_filename(std::string path)
+{
+ int beginIdx = path.rfind(PATH_SEPARATOR);
+ return path.substr(beginIdx + 1);
+}
+
+static void show_usage(std::string name)
+{
+ std::cerr << "Usage: " << get_filename(name) << " <option(s)> [SOURCE]\n" << std::endl
+ << "Options:" << std::endl
+ << "\t-h, --help\t\t Show this help message" << std::endl;
+}
+
+/*
+ * Basic console.log implementation with space-separated values,
+ * no substitution
+ */
+void Log(const FunctionCallbackInfo<Value>& args)
+{
+ Isolate* isolate = Isolate::GetCurrent();
+ HandleScope scope(isolate);
+
+ for (int i=0; i < args.Length(); i++)
+ {
+ if (i != 0)
+ std::cout << " ";
+ String::Utf8Value string(args[i]);
+ std::cout << *string;
+ }
+
+ std::cout << std::endl;
+
+ args.GetReturnValue().Set(v8::Null(isolate));
+}
+
+
+int main(int argc, char* argv[])
+{
+
+ std::string script_source;
+ char *filename = 0;
+
+ for (int i=1; i < argc; i++)
+ {
+ if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0))
+ {
+ show_usage(argv[0]);
+ return 0;
+ }
+ else
+ {
+ filename = argv[i];
+ }
+ }
+
+ if (!filename)
+ {
+ std::cerr << "Error: No source provided." << std::endl;
+ show_usage(argv[0]);
+ return 1;
+ }
+
+ try
+ {
+ script_source = get_file_contents(filename);
+ } catch (int errno)
+ {
+ perror("Error: ");
+ return 1;
+ }
+
+
+ efl::eina::js::compatibility_initialize();
+ v8::V8::SetFlagsFromCommandLine(&argc, const_cast<char**>(argv), true);
+
+ v8::Isolate* isolate = efl::eina::js::compatibility_isolate_new();
+ {
+ Isolate::Scope isolate_scope(isolate);
+ HandleScope handleScope(isolate);
+
+ Local<Context> context = Context::New(isolate, NULL);
+ Context::Scope context_scope(context);
+ context->Enter();
+
+ // Setup the console and log
+ Local<Object> console = Object::New(isolate);
+ Local<FunctionTemplate> log = FunctionTemplate::New(isolate, Log);
+ console->Set(String::NewFromUtf8(isolate, "log"), log->GetFunction());
+
+ Local<Object> global = context->Global();
+ global->Set(String::NewFromUtf8(isolate, "console"), console);
+
+ // Set up the efl exports; Needed to enter the context before this
+ // due to creating Objects instead of Objects Templates
+ // WIP: Commented out due to potential missing v8 platform implementation issues
+ // Local<Object> efl_exports = Object::New(isolate);
+ // global->Set(String::NewFromUtf8(isolate, "efl"), efl_exports);
+ // efl_js::init(efl_exports);
+
+ // And now the user's script
+ Local<String> source = String::NewFromUtf8(isolate, script_source.c_str());
+
+ Local<Script> script = Script::Compile(source);
+
+ TryCatch tryCatch(isolate);
+ Local<Value> result = script->Run();
+
+ if (result.IsEmpty())
+ {
+ Local<Value> exception = tryCatch.Exception();
+ String::Utf8Value exception_str(exception);
+ printf("Exception: %s\n", *exception_str);
+ }
+
+ }
+
+ V8::Dispose();
+ return 0;
+}