efl/src/bin/efl_js/efljspack

252 lines
7.2 KiB
Bash
Executable File

#!/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;
}