summaryrefslogtreecommitdiff
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-23 23:59:40 -0200
commita3db1dddd3ba67c81118f7f2c0bc753dc8aac551 (patch)
tree233ee1be7bfa299bff560207135d20940c4e411f /src/bin/efl_js
parent1a3cb45f1cc7fdf8d481879e6bd7349d9cb0b3fa (diff)
efl-js: JavaScript Eolian binding
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 @@
1#!/bin/sh
2':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"
3
4// Core node modules
5var path = require('path');
6var os = require('os');
7var zlib = require('zlib');
8var child_process = require('child_process');
9
10// 3rd party modules
11var fs = require('fs-extra');
12var getopt = require('node-getopt');
13var tar = require('tar');
14
15function make_error_cb(message)
16{
17 return function(e) {
18 console.error("Error %s: %s", message, e);
19 process.exit(1);
20 };
21}
22
23function remove_files(options)
24{
25 if (options.verbose)
26 console.log("Removing temporary files");
27
28 fs.remove(options.project_folder);
29}
30
31function run_project(options)
32{
33 if (options.verbose)
34 console.log("Running the project");
35
36 var current_dir = process.cwd();
37 process.chdir(options.project_root);
38
39 var proc = child_process.fork(options.metadata.Entry);
40 proc.on('exit', function(code){
41 if (options.verbose)
42 console.log('Child exited with code %s', code);
43 process.chdir(current_dir);
44 if (!options.keep)
45 remove_files(options);
46 });
47
48}
49
50function unpack_project_data(options)
51{
52 if (options.verbose)
53 console.log("Unpacking project sources and assets");
54
55 var datafile = path.join(options.project_folder, "data.tar.gz");
56 var project_root = path.join(options.project_folder, "root");
57
58 options.project_root = project_root;
59
60 var input = fs.createReadStream(datafile);
61 var unzipper = zlib.createGunzip();
62 var extractor = tar.Extract({path: project_root, strip: 0});
63
64 input.on('error', make_error_cb("reading package data file."));
65 extractor.on('error', make_error_cb("unpacking package data file."));
66 if (!("only-extract" in options))
67 extractor.on('end', function(){ run_project(options); });
68
69 input.pipe(unzipper)
70 unzipper.pipe(extractor);
71}
72
73function read_metadata(options)
74{
75 if (options.verbose)
76 console.log("Reading project metadata");
77
78 var project_folder = options.project_folder;
79 var metadata = JSON.parse(fs.readFileSync(path.join(project_folder, "meta.json")));
80
81 if (options.verbose)
82 console.log("Project: %s\nVersion: %s\nEntry point: %s", metadata.Name, metadata.Version, metadata.Entry);
83 if ("only-dump" in options)
84 process.exit(0);
85
86 options.metadata = metadata;
87
88 unpack_project_data(options);
89}
90
91function extract(filename, options)
92{
93 if (options.verbose)
94 console.log("Extracting ", filename, "with options ", options);
95
96 var project_id = path.basename(filename, ".epk");
97 var project_folder = path.join(options['temp-dir'], project_id);
98
99 options.project_folder = project_folder;
100 options.project_id = project_id;
101
102 var input = fs.createReadStream(filename);
103 var extractor = tar.Extract({path: options['temp-dir'], strip: 0});
104
105 input.on('error', make_error_cb("reading package file."));
106 extractor.on('error', make_error_cb("unpacking package file."));
107 extractor.on('end', function(){ read_metadata(options); });
108
109 input.pipe(extractor);
110}
111
112function main() {
113 var options = getopt.create([
114 ['d', 'only-dump', 'Only dump information about the package'],
115 ['e', 'only-extract', 'Only extract the package, do not run'],
116 ['h', 'help', 'Display this help'],
117 ['k', 'keep', 'Do not remove the files after exiting'],
118 ['t', 'temp-dir=ARG', 'Temporary dir to extract files'],
119 ['v', 'verbose', 'Print information messages'],
120 ]).bindHelp().parseSystem();
121
122 var filename = options.argv[0];
123 if (filename === undefined)
124 {
125 console.error("Must provide a package file.");
126 process.exit(1);
127 }
128
129 if (!('temp-dir' in options.options))
130 {
131 options.options["temp-dir"] = path.join(os.tmpdir(), "efljs_apps");
132 if (options.verbose)
133 console.log("Defaulting temp dir to ", options.options["temp-dir"]);
134 }
135
136 extract(filename, options.options);
137}
138
139main();
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 @@
1[Desktop Entry]
2Name=EFL JS package launcher
3Exec=efljslaunch %f
4Type=Application
5Categories=EFL
6Terminal=true
7MimeType=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 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
3 <mime-type type="application/x-efljspackage">
4 <comment xml:lang="en">EFL JS package</comment>
5 <glob pattern="*.epk"/>
6 </mime-type>
7</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 @@
1#!/bin/sh
2':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"
3
4var zlib = require('zlib');
5var path = require('path');
6
7// external dependencies
8var fs = require('fs-extra');
9var tar = require('tar');
10var fstream = require('fstream');
11var getopt = require('node-getopt');
12
13/*
14 * Packing a project.
15 * The efljs package has a similar format to debian packages. It is a
16 * tar package containing two files:
17 *
18 * meta.txt: Metadata information about this package.
19 * data.tar.gz: Gzipped data, with the project tree ready to be decompressed
20 * and run by the package launcher.
21 *
22 * During the build, a out/ directory is created in the project root to
23 * store the package and temporary files.
24 */
25
26// Creates a stub .project file and packs it.
27function pack_single(sourcepath, options)
28{
29 if (options.verbose)
30 console.log("Creating project file for single file app", sourcepath);
31
32 var dir_name = path.dirname(fs.realpathSync(sourcepath));
33 var filename = path.basename(sourcepath);
34 var projectRegex = /^(.*).js$/g;
35 var project_name = projectRegex.exec(filename)[1];
36
37 if (!validade_project_name(project_name))
38 {
39 console.error("Invalid project name. Must start with a letter.");
40 process.exit(0);
41 }
42
43 var project_filename = path.join(dir_name, project_name + ".project");
44
45 var fd = fs.openSync(project_filename, 'w');
46
47 var jsonData = {};
48
49 jsonData["Name"] = project_name;
50 jsonData["Entry"] = filename;
51 jsonData["Sources"] = [[filename, '.']];
52 jsonData["Version"] = "0.1";
53
54 fs.writeSync(fd, JSON.stringify(jsonData, null, 2));
55
56 fs.closeSync(fd);
57
58 pack_project(project_filename, options);
59
60}
61
62function generate_build_info(configuration, project_file, options)
63{
64 build_info = {};
65
66 // project == project_dir
67 // /out == build_dir
68 // /data == data_dir
69 // /name-version == package_dir
70
71 build_info.package_id = configuration.Name + "-" + configuration.Version;
72 build_info.project_dir = path.dirname(project_file);
73 build_info.build_dir = path.join(build_info.project_dir, "out");
74 build_info.data_dir = path.join(build_info.build_dir, "data");
75 build_info.package_dir = path.join(build_info.build_dir, build_info.package_id);
76 build_info.data_file = path.join(build_info.package_dir, "data.tar.gz");
77 build_info.package_file = path.join(build_info.build_dir, build_info.package_id + ".epk")
78 build_info.metadata_file = path.join(build_info.package_dir, "meta.json");
79
80 if (options.verbose)
81 {
82 console.log("Project id: ", build_info.package_id);
83 console.log("Project source dir: ", build_info.project_dir);
84 console.log("Project build dir: ", build_info.build_dir);
85 console.log("Project data dir:", build_info.data_dir);
86 console.log("Project package dir:", build_info.package_dir);
87 }
88
89 return build_info;
90
91}
92
93// Project names must start with a letter and contain only
94// letters, digits and underscores.
95function validade_project_name(name)
96{
97 return (/^[a-zA-Z][\w-]*$/).test(name)
98}
99
100function pack_project(project_file, options)
101{
102 if (options.verbose)
103 console.log("Packing project from project file ", project_file);
104
105 var configuration = JSON.parse(fs.readFileSync(project_file));
106
107 if (!validade_project_name(configuration.Name))
108 {
109 console.error("Invalid project name. Must start with a letter.");
110 process.exit(0);
111 }
112
113 var build_info = generate_build_info(configuration, project_file, options);
114
115 try
116 {
117 fs.mkdirSync(build_info.build_dir);
118 fs.mkdirSync(build_info.data_dir);
119 fs.mkdirSync(build_info.package_dir);
120 }
121 catch (e)
122 {
123 console.warn("Warning: Project output directories not empty.");
124 }
125
126 create_metadata_file(configuration, build_info, options);
127
128 // If not explicitly named on configuration, add the entire directory
129 if (!('Sources' in configuration))
130 {
131 generate_source_list(configuration, build_info.project_dir, options);
132 }
133
134 create_project_tree(configuration.Sources, build_info, options);
135
136 pack_data_dir(build_info, options);
137}
138
139function create_project_tree(sources, build_info, options)
140{
141 for (var i = sources.length - 1; i >= 0; i--) {
142 if (options.verbose)
143 console.log("Adding file ", sources[i], "to package.");
144 var source_file = path.join(build_info.project_dir, sources[i][0]);
145 var destination_dir = path.join(build_info.data_dir, sources[i][1]);
146 var destination_filename = path.basename(source_file);
147 var destination_file = path.join(destination_dir, destination_filename);
148
149 fs.copySync(source_file, destination_file);
150 };
151}
152
153function generate_source_list(configuration, project_dir, options)
154{
155 console.log("Generating source list for project dir", build_info.project_dir);
156 var dir_entries = fs.readdirSync(project_dir);
157 var sources = [];
158
159 dir_entries.forEach(function(entry){
160 if (entry == "out")
161 return;
162 sources.push([entry, "."]);
163 });
164 configuration.Sources = sources;
165}
166
167function create_metadata_file(configuration, build_info, options)
168{
169 if (options.verbose)
170 console.log("Creating metadata file", build_info.metadata_file);
171
172 var metadata = {};
173
174 metadata.Name = configuration.Name;
175 metadata.Entry = configuration.Entry;
176 metadata.Version = configuration.Version;
177
178 var output = fs.createWriteStream(build_info.metadata_file);
179 output.write(JSON.stringify(metadata, null, 2));
180 output.close();
181}
182
183function pack_data_dir(build_info, options)
184{
185 if (options.verbose)
186 console.log("Packing data...");
187
188 pack_directory(build_info.data_dir, build_info.data_file, true, true, function(){
189 if (options.verbose)
190 console.log("Packed data");
191 pack_final_package(build_info, options);
192 });
193}
194
195function pack_final_package(build_info, options)
196{
197 if (options.verbose)
198 console.log("Creating package ", build_info.package_file);
199 pack_directory(build_info.package_dir, build_info.package_file, false, false, function(){
200 if (options.verbose)
201 console.log("Created project package.");
202 });
203}
204
205function pack_directory(source_dir, target_file, strip_base_dir, should_gzip, callback)
206{
207 var output = fs.createWriteStream(target_file);
208 var packer = tar.Pack({fromBase: strip_base_dir == true});
209 if (callback != undefined)
210 output.on('close', callback);
211
212 var reader = fstream.Reader({path: source_dir, type: "Directory"});
213 var destStr = reader.pipe(packer);
214 if(should_gzip)
215 destStr = destStr.pipe(zlib.createGzip());
216 destStr.pipe(output);
217}
218
219function main()
220{
221
222 var options = getopt.create([
223 ['v', 'verbose', 'Explain what is being done'],
224 ['h', 'help', 'Display this help']
225 ]).bindHelp().parseSystem();
226
227 filename = options.argv[0];
228
229 if (typeof filename === 'undefined')
230 {
231 console.error('Must provide a valid js or project file.');
232 process.exit(1);
233 }
234
235 if (endsWith(filename, ".js"))
236 {
237 pack_single(filename, options.options);
238 }
239 else if (endsWith(filename, ".project"))
240 {
241 pack_project(filename, options.options);
242 }
243}
244
245main();
246
247//// Helper functions
248function endsWith(str, suffix)
249{
250 return str.indexOf(suffix, str.length - suffix.length) !== -1;
251}
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 @@
1#ifdef HAVE_CONFIG_H
2#include <config.h>
3#endif
4
5#include <iostream>
6#include <fstream>
7#include <sstream>
8#include <string>
9#include <cerrno>
10
11#include <Eo_Js.hh>
12#include <Eina.hh>
13#include <Eo.hh>
14// #include <efl_js.hh>
15
16using namespace std;
17using namespace v8;
18
19const char PATH_SEPARATOR =
20#ifdef _WIN32
21 '\\';
22#else
23 '/';
24#endif
25
26static std::string get_file_contents(const char *filename) {
27 std::ifstream in(filename, std::ios::in);
28 if (in) {
29 std::ostringstream contents;
30 contents << in.rdbuf();
31 in.close();
32 return contents.str();
33 } else {
34 throw(errno);
35 }
36}
37
38static std::string get_filename(std::string path)
39{
40 int beginIdx = path.rfind(PATH_SEPARATOR);
41 return path.substr(beginIdx + 1);
42}
43
44static void show_usage(std::string name)
45{
46 std::cerr << "Usage: " << get_filename(name) << " <option(s)> [SOURCE]\n" << std::endl
47 << "Options:" << std::endl
48 << "\t-h, --help\t\t Show this help message" << std::endl;
49}
50
51/*
52 * Basic console.log implementation with space-separated values,
53 * no substitution
54 */
55void Log(const FunctionCallbackInfo<Value>& args)
56{
57 Isolate* isolate = Isolate::GetCurrent();
58 HandleScope scope(isolate);
59
60 for (int i=0; i < args.Length(); i++)
61 {
62 if (i != 0)
63 std::cout << " ";
64 String::Utf8Value string(args[i]);
65 std::cout << *string;
66 }
67
68 std::cout << std::endl;
69
70 args.GetReturnValue().Set(v8::Null(isolate));
71}
72
73
74int main(int argc, char* argv[])
75{
76
77 std::string script_source;
78 char *filename = 0;
79
80 for (int i=1; i < argc; i++)
81 {
82 if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0))
83 {
84 show_usage(argv[0]);
85 return 0;
86 }
87 else
88 {
89 filename = argv[i];
90 }
91 }
92
93 if (!filename)
94 {
95 std::cerr << "Error: No source provided." << std::endl;
96 show_usage(argv[0]);
97 return 1;
98 }
99
100 try
101 {
102 script_source = get_file_contents(filename);
103 } catch (int errno)
104 {
105 perror("Error: ");
106 return 1;
107 }
108
109
110 efl::eina::js::compatibility_initialize();
111 v8::V8::SetFlagsFromCommandLine(&argc, const_cast<char**>(argv), true);
112
113 v8::Isolate* isolate = efl::eina::js::compatibility_isolate_new();
114 {
115 Isolate::Scope isolate_scope(isolate);
116 HandleScope handleScope(isolate);
117
118 Local<Context> context = Context::New(isolate, NULL);
119 Context::Scope context_scope(context);
120 context->Enter();
121
122 // Setup the console and log
123 Local<Object> console = Object::New(isolate);
124 Local<FunctionTemplate> log = FunctionTemplate::New(isolate, Log);
125 console->Set(String::NewFromUtf8(isolate, "log"), log->GetFunction());
126
127 Local<Object> global = context->Global();
128 global->Set(String::NewFromUtf8(isolate, "console"), console);
129
130 // Set up the efl exports; Needed to enter the context before this
131 // due to creating Objects instead of Objects Templates
132 // WIP: Commented out due to potential missing v8 platform implementation issues
133 // Local<Object> efl_exports = Object::New(isolate);
134 // global->Set(String::NewFromUtf8(isolate, "efl"), efl_exports);
135 // efl_js::init(efl_exports);
136
137 // And now the user's script
138 Local<String> source = String::NewFromUtf8(isolate, script_source.c_str());
139
140 Local<Script> script = Script::Compile(source);
141
142 TryCatch tryCatch(isolate);
143 Local<Value> result = script->Run();
144
145 if (result.IsEmpty())
146 {
147 Local<Value> exception = tryCatch.Exception();
148 String::Utf8Value exception_str(exception);
149 printf("Exception: %s\n", *exception_str);
150 }
151
152 }
153
154 V8::Dispose();
155 return 0;
156}