// Copyright (c) 2013 Perforce Software. All rights reserved. var _ = require("underscore"); var AdmZip = require("adm-zip"); var async = require("async"); var child_process = require("child_process"); var fs = require("fs"); var mkdirp = require("mkdirp"); var os = require("os"); var path = require("path"); var ServiceUtil = require("./service_util"); // Options // - grunt (required) // - version // function P4DUtils(options) { options = options || {}; this.grunt = options.grunt; this.version = options.version || "r13.2"; this.p4DepotFormat = options.p4DepotFormat || "//builds/<%= version %>/p4-bin/bin.<%= arch %>/p4d"; this.devZip = options.devZip || __dirname + "/dev.zip"; this.workDir = options.workDir || "work"; this.appCacheDir = options.appCacheDir || path.resolve(this.workDir, "cache"); this.p4dRootDir = options.p4dRootDir || path.resolve(this.workDir, "p4droot"); this.args = options.args || ["-p", "1666", "-r", this.p4dRootDir, "-v", "server=1"]; this.serviceUtil = new ServiceUtil({ grunt: options.grunt, workDir: this.workDir }); } _.extend(P4DUtils.prototype, { // TODO we probably want options, like version, etc. startP4d: function(done) { var self = this; var steps = [ _.bind(this.serviceUtil.killIfRunning, this.serviceUtil, "p4d", "SIGTERM"), _.bind(this.rebuildP4dRoot, this), _.bind(this.locateP4d, this, null), function(p4dExecutable, cb) { self.serviceUtil.spawnInWork("p4d", p4dExecutable, self.args, cb); } ]; async.waterfall(steps, done); }, stopP4d: function(callback) { this.serviceUtil.killIfRunning("p4d", "SIGTERM", callback); }, // We save actual p4d executable names for the current architecture by // version number, e.g., "p4d-r13.2". This allows the same server to be // used against multiple versions of p4d. // // We do need to have the "p4" command in the path with username and // password set. // // Options: // - version: Specify the version to use, default to "r13.2" // - arch: Override the architecture, if not set, we guess it. locateP4d: function(options, callback) { var self = this; options = options || {}; if (!options.version) options.version = this.version; if (!options.arch) options.arch = getArch(); var p4dPath = path.join(this.appCacheDir, "p4d-" + options.version); fs.exists(p4dPath, function(exists) { if (exists) return callback(null, p4dPath); mkdirp.sync(self.appCacheDir); var depotLocation = _.template(self.p4DepotFormat, { arch: options.arch, version: options.version }); var cmd = "p4 print -o " + p4dPath + " " + depotLocation; child_process.exec(cmd, function(err, stdout, stderr) { if (err) { console.log("Cache of " + depotLocation + " to " + p4dPath + " failed.", stdout, stderr); return callback(err); } callback(null, p4dPath); }); }); }, // Deletes the working directory and recreates it, then unzips the dev.zip // file there. // // We assume the p4d process is not running and there are no locks on files. rebuildP4dRoot: function(callback) { rmDir(this.p4dRootDir); mkdirp.sync(this.p4dRootDir); var zip = new AdmZip(this.devZip); zip.extractAllTo(this.p4dRootDir, true); callback(); }, updateDev: function(done) { var cmd = "zip " + this.devZip + " -r *"; var options = { cwd: this.p4dRootDir }; console.log('cmd', cmd); console.log("options", options); child_process.exec(cmd, options, function(err, stdout, stderr) { if (err) { grunt.fail(err + "\n" + stdout + "\n" + stderr); return done(err); } done(); }); }, updateToUnicode: function(callback) { var self = this; function execP4dXi(p4dPath, epxCallback) { var cmd = p4dPath + " -xi -r " + self.p4dRootDir; child_process.exec(cmd, function(err, stdout, stderr) { if (err) { console.log("Upgrade to unicode mode failed", err, stdout, stderr); return epxCallback(err); } epxCallback(); }); } var steps = [ _.bind(this.locateP4d, this, null), execP4dXi ]; async.waterfall(steps, callback); } }); // Map the local os.platform and os.arch settings to the string key we use // for these settings for builds. function getArch() { var arch; if (os.platform() === "darwin") { arch = "darwin90x86"; if (os.arch() === "x64") arch += "_64"; } else if (os.platform() === "linux") { arch = "linux26x86"; if (os.arch() === "x64") arch += "_64"; } else { throw "do not know arch setting for platform " + os.platform(); } return arch; } // A simple synchronous rm -rf equivalent. function rmDir(dirPath) { var files; try { files = fs.readdirSync(dirPath); } catch (e) { return; } if (files.length > 0) for (var i = 0; i < files.length; i++) { var filePath = dirPath + '/' + files[i]; if (fs.statSync(filePath).isFile()) fs.unlinkSync(filePath); else rmDir(filePath); } fs.rmdirSync(dirPath); } module.exports = P4DUtils;
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 10513 | tjuricek |
A basic experiment with using the C++ API in Go May not work completely. I haven't messed with this in over a year. The Go tools may have come a long ways as well, and we may be able to automate things more directly in the typical toolkit then having to use node. |