|
|
#!/usr/bin/env node
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Tencent is pleased to support the open source community by making Tars available.
|
||
|
|
*
|
||
|
|
* Copyright (C) 2016THL A29 Limited, a Tencent company. All rights reserved.
|
||
|
|
*
|
||
|
|
* Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
|
||
|
|
* in compliance with the License. You may obtain a copy of the License at
|
||
|
|
*
|
||
|
|
* https://opensource.org/licenses/BSD-3-Clause
|
||
|
|
*
|
||
|
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||
|
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||
|
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||
|
|
* specific language governing permissions and limitations under the License.
|
||
|
|
*/
|
||
|
|
|
||
|
|
'use strict'
|
||
|
|
|
||
|
|
var fs = require('fs');
|
||
|
|
var path = require('path');
|
||
|
|
var events = require('events');
|
||
|
|
var spawn = require('child_process').spawn;
|
||
|
|
var zlib = require('zlib');
|
||
|
|
|
||
|
|
var async = require('async');
|
||
|
|
var fse = require('fs-extra');
|
||
|
|
var fstream = require('fstream');
|
||
|
|
var tar = require('tar');
|
||
|
|
|
||
|
|
module.exports = exports = new events();
|
||
|
|
|
||
|
|
var tmpName = '';
|
||
|
|
|
||
|
|
var config = exports.config = {
|
||
|
|
exclude : ['.svn', '.git', '_svn', '_git', '.tgz', '_tmp_dir', '.idea'],
|
||
|
|
level : 6,
|
||
|
|
memLevel : 6,
|
||
|
|
maxBuffer : 500 * 1024
|
||
|
|
};
|
||
|
|
|
||
|
|
var execNPM = function(command, cwd, options, cb) {
|
||
|
|
fs.exists(path.join(cwd, 'package.json'), function(exists) {
|
||
|
|
var npm;
|
||
|
|
|
||
|
|
command = command.trim().split(' ');
|
||
|
|
if (!exists && command.length <= 1) {
|
||
|
|
return cb();
|
||
|
|
}
|
||
|
|
|
||
|
|
npm = spawn(process.platform === 'win32' ? 'npm.cmd' : 'npm', command, {cwd : cwd, stdio: 'inherit'});
|
||
|
|
|
||
|
|
npm.on('exit', function(code) {
|
||
|
|
var err;
|
||
|
|
|
||
|
|
if (code !== 0) {
|
||
|
|
err = new Error('npm return code: "' + code + '"');
|
||
|
|
}
|
||
|
|
|
||
|
|
fs.unlink(path.join(cwd, 'npm-debug.log'), function() {
|
||
|
|
cb(err);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// 初始化目录结构
|
||
|
|
var mkdir = function(name, dir, cb) {
|
||
|
|
exports.emit('progress:start', 'Creating directory structure');
|
||
|
|
|
||
|
|
fs.stat(dir, function(err, stat) {
|
||
|
|
tmpName = '_' + name + '_' + Date.now();
|
||
|
|
|
||
|
|
if (err || !stat.isDirectory()) {
|
||
|
|
cb(new Error('Not a directory'));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
fs.mkdir(path.join(dir, tmpName), function(err) {
|
||
|
|
if (err) {
|
||
|
|
cb(err);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
fs.readdir(dir, function(err, files) {
|
||
|
|
if (err) {
|
||
|
|
cb(err);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
async.mapSeries([
|
||
|
|
path.join(dir, tmpName, name),
|
||
|
|
path.join(dir, tmpName, name, name),
|
||
|
|
path.join(dir, tmpName, name, name, 'src'),
|
||
|
|
path.join(dir, tmpName, name, name, 'tars_nodejs'),
|
||
|
|
path.join(dir, tmpName, name, name, 'tars_nodejs', 'node-agent'),
|
||
|
|
path.join(dir, tmpName, name, name, 'tars_nodejs', 'node-agent', 'node_modules')
|
||
|
|
], fs.mkdir.bind(fs), function(err) {
|
||
|
|
if (err) {
|
||
|
|
cb(err);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
async.map(files.filter(function(file) {
|
||
|
|
return file !== tmpName;
|
||
|
|
}).map(function(file) {
|
||
|
|
return [path.join(dir, file), path.join(dir, tmpName, name, name, 'src', file)];
|
||
|
|
}), function(item, cb) {
|
||
|
|
fse.copy(item[0], item[1], cb);
|
||
|
|
}, function(err) {
|
||
|
|
if (err) {
|
||
|
|
cb(err);
|
||
|
|
} else {
|
||
|
|
exports.emit('progress:end', 'Created directory');
|
||
|
|
cb();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// 拷贝 node 可执行文件
|
||
|
|
var cp = function(name, dir, cb) {
|
||
|
|
exports.emit('progress:start', 'Copying node exec file');
|
||
|
|
|
||
|
|
fse.copy(process.execPath, path.join(dir, tmpName, name, name, 'tars_nodejs', 'node'), function(err) {
|
||
|
|
if (err) {
|
||
|
|
cb(err);
|
||
|
|
} else {
|
||
|
|
exports.emit('progress:end', 'Copied file');
|
||
|
|
cb();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// 安装 node-agent
|
||
|
|
var install = function(name, dir, cb) {
|
||
|
|
exports.emit('progress:start', 'Installing node-agent');
|
||
|
|
|
||
|
|
var cwd = path.join(dir, tmpName, name, name, 'tars_nodejs', 'node-agent');
|
||
|
|
|
||
|
|
execNPM('install --global-style @tars/node-agent', cwd, null, function(err, stdout, stderr) {
|
||
|
|
if (err) {
|
||
|
|
cb(err, stdout, stderr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
fs.exists(path.join(cwd, 'node_modules', '@tars', 'node-agent'), function(exists) {
|
||
|
|
if (!exists) {
|
||
|
|
cb(true, stdout, stderr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
fs.rename(path.join(cwd, 'node_modules', '@tars', 'node-agent'), cwd + '2', function(err) {
|
||
|
|
if (err) {
|
||
|
|
cb(true, stdout, stderr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
fse.remove(cwd, function(err) {
|
||
|
|
if (err) {
|
||
|
|
cb(true, stdout, stderr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
fs.rename(cwd + '2', cwd, function(err) {
|
||
|
|
if (err) {
|
||
|
|
cb(true, stdout, stderr);
|
||
|
|
} else {
|
||
|
|
exports.emit('progress:end', 'Installed node-agent');
|
||
|
|
cb(null, stdout, stderr);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// 安装 src 中的依赖项
|
||
|
|
var init = function(name, dir, cb) {
|
||
|
|
exports.emit('progress:start', 'Installing dependency');
|
||
|
|
|
||
|
|
var cwd = path.join(dir, tmpName, name, name, 'src');
|
||
|
|
|
||
|
|
fs.exists(path.join(cwd, 'package.json'), function(exists) {
|
||
|
|
if (!exists) {
|
||
|
|
exports.emit('progress:end', 'Not found package.json');
|
||
|
|
return cb();
|
||
|
|
}
|
||
|
|
|
||
|
|
execNPM('install --production', cwd, null, function(err, stdout, stderr) {
|
||
|
|
if (!err) {
|
||
|
|
exports.emit('progress:end', 'Installed dependency');
|
||
|
|
}
|
||
|
|
|
||
|
|
cb(err, stdout, stderr);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// 重新编译
|
||
|
|
var rebuild = function(name, dir, cb) {
|
||
|
|
exports.emit('progress:start', 'Building C/C++ modules');
|
||
|
|
|
||
|
|
var cwd = path.join(dir, tmpName, name, name, 'src');
|
||
|
|
execNPM('rebuild', cwd, null, function(err, stdout, stderr) {
|
||
|
|
if (err) {
|
||
|
|
cb(err, stdout, stderr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
fs.exists(path.join(cwd, 'binding.gyp'), function(exists) {
|
||
|
|
var gyp;
|
||
|
|
|
||
|
|
if (!exists) {
|
||
|
|
exports.emit('progress:end', 'Not found C/C++ modules');
|
||
|
|
return cb();
|
||
|
|
}
|
||
|
|
|
||
|
|
gyp = spawn(process.platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp', ['rebuild'], {cwd : cwd, stdio: 'inherit'});
|
||
|
|
|
||
|
|
gyp.stdout.pipe(process.stdout);
|
||
|
|
gyp.stderr.pipe(process.stderr);
|
||
|
|
|
||
|
|
gyp.on('exit', function(code) {
|
||
|
|
var err;
|
||
|
|
|
||
|
|
if (code !== 0) {
|
||
|
|
err = new Error('node-gyp return code: "' + code + '"');
|
||
|
|
} else {
|
||
|
|
exports.emit('progress:end', 'Built C/C++ modules');
|
||
|
|
}
|
||
|
|
|
||
|
|
cb(err);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// 检查依赖状态
|
||
|
|
var check = function(name, dir, cb) {
|
||
|
|
exports.emit('progress:start', 'Checking outdated dependency');
|
||
|
|
|
||
|
|
execNPM('outdated', path.join(dir, tmpName, name, name, 'src'), null, function(err, stdout, stderr) {
|
||
|
|
if (!err) {
|
||
|
|
exports.emit('progress:end', 'Checked outdated dependency');
|
||
|
|
}
|
||
|
|
|
||
|
|
cb(err, stdout, stderr);
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// 生成tar.gz, tgz包
|
||
|
|
var pack = function(name, dir, cb) {
|
||
|
|
exports.emit('progress:start', 'Making deploy package');
|
||
|
|
|
||
|
|
var dirDest = fs.createWriteStream(path.join(dir, name + '.tgz')),
|
||
|
|
packer = tar.Pack({
|
||
|
|
noProprietary: true
|
||
|
|
}),
|
||
|
|
gzip = zlib.createGzip({
|
||
|
|
level: config.level,
|
||
|
|
memLevel: config.memLevel
|
||
|
|
}),
|
||
|
|
reader = fstream.Reader({
|
||
|
|
path: path.join(dir, tmpName, name),
|
||
|
|
type: "Directory",
|
||
|
|
filter : function(entry) {
|
||
|
|
if (entry.props.Directory || path.extname(entry.props.basename) === '') {
|
||
|
|
entry.props.mode = 493; // 0755
|
||
|
|
}
|
||
|
|
|
||
|
|
return !config.exclude.some(function(name) {
|
||
|
|
if (entry.props.basename.indexOf(name) !== -1) {
|
||
|
|
return entry.props.basename.indexOf(name) === entry.props.basename.length - name.length;
|
||
|
|
} else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
complete = function(err) {
|
||
|
|
if (!err) {
|
||
|
|
exports.emit('progress:end', 'Made deploy package');
|
||
|
|
}
|
||
|
|
|
||
|
|
cb(err);
|
||
|
|
};
|
||
|
|
|
||
|
|
reader.on('error', complete);
|
||
|
|
packer.on('error', complete);
|
||
|
|
gzip.on('error', complete);
|
||
|
|
dirDest.on('error', complete);
|
||
|
|
|
||
|
|
dirDest.on('close', complete);
|
||
|
|
|
||
|
|
reader.pipe(packer).pipe(gzip).pipe(dirDest);
|
||
|
|
};
|
||
|
|
|
||
|
|
// 删除临时文件
|
||
|
|
var clean = function(name, dir, cb) {
|
||
|
|
exports.emit('progress:start', 'Cleaning temp files');
|
||
|
|
fse.remove(path.join(dir, tmpName), function(err) {
|
||
|
|
if (!err) {
|
||
|
|
exports.emit('progress:end', 'Cleaned temp files');
|
||
|
|
}
|
||
|
|
|
||
|
|
if (typeof cb === 'function') {
|
||
|
|
cb(err);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
exports.STEP_COUNT = 8;
|
||
|
|
|
||
|
|
exports.make = function(name, dir) {
|
||
|
|
var wrapper = function(fn) {
|
||
|
|
return function(callback) {
|
||
|
|
fn(name, dir, callback);
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
async.series([mkdir, cp, install, init, rebuild, check, pack, clean].map(function(fn) {
|
||
|
|
return wrapper(fn);
|
||
|
|
}), function(err, results) {
|
||
|
|
if (err) {
|
||
|
|
exports.emit('error', err);
|
||
|
|
clean(name, dir);
|
||
|
|
} else {
|
||
|
|
exports.emit('done', path.join(dir, name + '.tgz'));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
};
|