package_task.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /*
  2. * Jake JavaScript build tool
  3. * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. let path = require('path');
  19. let fs = require('fs');
  20. let exec = require('child_process').exec;
  21. let FileList = require('filelist').FileList;
  22. /**
  23. @name jake
  24. @namespace jake
  25. */
  26. /**
  27. @name jake.PackageTask
  28. @constructor
  29. @description Instantiating a PackageTask creates a number of Jake
  30. Tasks that make packaging and distributing your software easy.
  31. @param {String} name The name of the project
  32. @param {String} version The current project version (will be
  33. appended to the project-name in the package-archive
  34. @param {Function} definition Defines the contents of the package,
  35. and format of the package-archive. Will be executed on the instantiated
  36. PackageTask (i.e., 'this', will be the PackageTask instance),
  37. to set the various instance-propertiess.
  38. @example
  39. let t = new jake.PackageTask('rous', 'v' + version, function () {
  40. let files = [
  41. 'Capfile'
  42. , 'Jakefile'
  43. , 'README.md'
  44. , 'package.json'
  45. , 'app/*'
  46. , 'bin/*'
  47. , 'config/*'
  48. , 'lib/*'
  49. , 'node_modules/*'
  50. ];
  51. this.packageFiles.include(files);
  52. this.packageFiles.exclude('node_modules/foobar');
  53. this.needTarGz = true;
  54. });
  55. */
  56. let PackageTask = function () {
  57. let args = Array.prototype.slice.call(arguments);
  58. let name = args.shift();
  59. let version = args.shift();
  60. let definition = args.pop();
  61. let prereqs = args.pop() || []; // Optional
  62. prereqs = [].concat(prereqs); // Accept string or list
  63. /**
  64. @name jake.PackageTask#name
  65. @public
  66. @type {String}
  67. @description The name of the project
  68. */
  69. this.name = name;
  70. /**
  71. @name jake.PackageTask#version
  72. @public
  73. @type {String}
  74. @description The project version-string
  75. */
  76. this.version = version;
  77. /**
  78. @name jake.PackageTask#prereqs
  79. @public
  80. @type {Array}
  81. @description Tasks to run before packaging
  82. */
  83. this.prereqs = prereqs;
  84. /**
  85. @name jake.PackageTask#packageDir
  86. @public
  87. @type {String='pkg'}
  88. @description The directory-name to use for packaging the software
  89. */
  90. this.packageDir = 'pkg';
  91. /**
  92. @name jake.PackageTask#packageFiles
  93. @public
  94. @type {jake.FileList}
  95. @description The list of files and directories to include in the
  96. package-archive
  97. */
  98. this.packageFiles = new FileList();
  99. /**
  100. @name jake.PackageTask#needTar
  101. @public
  102. @type {Boolean=false}
  103. @description If set to true, uses the `tar` utility to create
  104. a gzip .tgz archive of the package
  105. */
  106. this.needTar = false;
  107. /**
  108. @name jake.PackageTask#needTarGz
  109. @public
  110. @type {Boolean=false}
  111. @description If set to true, uses the `tar` utility to create
  112. a gzip .tar.gz archive of the package
  113. */
  114. this.needTarGz = false;
  115. /**
  116. @name jake.PackageTask#needTarBz2
  117. @public
  118. @type {Boolean=false}
  119. @description If set to true, uses the `tar` utility to create
  120. a bzip2 .bz2 archive of the package
  121. */
  122. this.needTarBz2 = false;
  123. /**
  124. @name jake.PackageTask#needJar
  125. @public
  126. @type {Boolean=false}
  127. @description If set to true, uses the `jar` utility to create
  128. a .jar archive of the package
  129. */
  130. this.needJar = false;
  131. /**
  132. @name jake.PackageTask#needZip
  133. @public
  134. @type {Boolean=false}
  135. @description If set to true, uses the `zip` utility to create
  136. a .zip archive of the package
  137. */
  138. this.needZip = false;
  139. /**
  140. @name jake.PackageTask#manifestFile
  141. @public
  142. @type {String=null}
  143. @description Can be set to point the `jar` utility at a manifest
  144. file to use in a .jar archive. If unset, one will be automatically
  145. created by the `jar` utility. This path should be relative to the
  146. root of the package directory (this.packageDir above, likely 'pkg')
  147. */
  148. this.manifestFile = null;
  149. /**
  150. @name jake.PackageTask#tarCommand
  151. @public
  152. @type {String='tar'}
  153. @description The shell-command to use for creating tar archives.
  154. */
  155. this.tarCommand = 'tar';
  156. /**
  157. @name jake.PackageTask#jarCommand
  158. @public
  159. @type {String='jar'}
  160. @description The shell-command to use for creating jar archives.
  161. */
  162. this.jarCommand = 'jar';
  163. /**
  164. @name jake.PackageTask#zipCommand
  165. @public
  166. @type {String='zip'}
  167. @description The shell-command to use for creating zip archives.
  168. */
  169. this.zipCommand = 'zip';
  170. /**
  171. @name jake.PackageTask#archiveNoBaseDir
  172. @public
  173. @type {Boolean=false}
  174. @description Simple option for performing the archive on the
  175. contents of the directory instead of the directory itself
  176. */
  177. this.archiveNoBaseDir = false;
  178. /**
  179. @name jake.PackageTask#archiveChangeDir
  180. @public
  181. @type {String=null}
  182. @description Equivalent to the '-C' command for the `tar` and `jar`
  183. commands. ("Change to this directory before adding files.")
  184. */
  185. this.archiveChangeDir = null;
  186. /**
  187. @name jake.PackageTask#archiveContentDir
  188. @public
  189. @type {String=null}
  190. @description Specifies the files and directories to include in the
  191. package-archive. If unset, this will default to the main package
  192. directory -- i.e., name + version.
  193. */
  194. this.archiveContentDir = null;
  195. if (typeof definition == 'function') {
  196. definition.call(this);
  197. }
  198. this.define();
  199. };
  200. PackageTask.prototype = new (function () {
  201. let _compressOpts = {
  202. Tar: {
  203. ext: '.tgz',
  204. flags: 'czf',
  205. cmd: 'tar'
  206. },
  207. TarGz: {
  208. ext: '.tar.gz',
  209. flags: 'czf',
  210. cmd: 'tar'
  211. },
  212. TarBz2: {
  213. ext: '.tar.bz2',
  214. flags: 'cjf',
  215. cmd: 'tar'
  216. },
  217. Jar: {
  218. ext: '.jar',
  219. flags: 'cf',
  220. cmd: 'jar'
  221. },
  222. Zip: {
  223. ext: '.zip',
  224. flags: 'qr',
  225. cmd: 'zip'
  226. }
  227. };
  228. this.define = function () {
  229. let self = this;
  230. let packageDirPath = this.packageDirPath();
  231. let compressTaskArr = [];
  232. desc('Build the package for distribution');
  233. task('package', self.prereqs.concat(['clobberPackage', 'buildPackage']));
  234. // Backward-compat alias
  235. task('repackage', ['package']);
  236. task('clobberPackage', function () {
  237. jake.rmRf(self.packageDir, {silent: true});
  238. });
  239. desc('Remove the package');
  240. task('clobber', ['clobberPackage']);
  241. let doCommand = function (p) {
  242. let filename = path.resolve(self.packageDir + '/' + self.packageName() +
  243. _compressOpts[p].ext);
  244. if (process.platform == 'win32') {
  245. // Windows full path may have drive letter, which is going to cause
  246. // namespace problems, so strip it.
  247. if (filename.length > 2 && filename[1] == ':') {
  248. filename = filename.substr(2);
  249. }
  250. }
  251. compressTaskArr.push(filename);
  252. file(filename, [packageDirPath], function () {
  253. let cmd;
  254. let opts = _compressOpts[p];
  255. // Directory to move to when doing the compression-task
  256. // Changes in the case of zip for emulating -C option
  257. let chdir = self.packageDir;
  258. // Save the current dir so it's possible to pop back up
  259. // after compressing
  260. let currDir = process.cwd();
  261. let archiveChangeDir;
  262. let archiveContentDir;
  263. if (self.archiveNoBaseDir) {
  264. archiveChangeDir = self.packageName();
  265. archiveContentDir = '.';
  266. }
  267. else {
  268. archiveChangeDir = self.archiveChangeDir;
  269. archiveContentDir = self.archiveContentDir;
  270. }
  271. cmd = self[opts.cmd + 'Command'];
  272. cmd += ' -' + opts.flags;
  273. if (opts.cmd == 'jar' && self.manifestFile) {
  274. cmd += 'm';
  275. }
  276. // The name of the archive to create -- use full path
  277. // so compression can be performed from a different dir
  278. // if needed
  279. cmd += ' ' + filename;
  280. if (opts.cmd == 'jar' && self.manifestFile) {
  281. cmd += ' ' + self.manifestFile;
  282. }
  283. // Where to perform the compression -- -C option isn't
  284. // supported in zip, so actually do process.chdir for this
  285. if (archiveChangeDir) {
  286. if (opts.cmd == 'zip') {
  287. chdir = path.join(chdir, archiveChangeDir);
  288. }
  289. else {
  290. cmd += ' -C ' + archiveChangeDir;
  291. }
  292. }
  293. // Where to get the archive content
  294. if (archiveContentDir) {
  295. cmd += ' ' + archiveContentDir;
  296. }
  297. else {
  298. cmd += ' ' + self.packageName();
  299. }
  300. // Move into the desired dir (usually packageDir) to compress
  301. // Return back up to the current dir after the exec
  302. process.chdir(chdir);
  303. exec(cmd, function (err, stdout, stderr) {
  304. if (err) { throw err; }
  305. // Return back up to the starting directory (see above,
  306. // before exec)
  307. process.chdir(currDir);
  308. complete();
  309. });
  310. }, {async: true});
  311. };
  312. for (let p in _compressOpts) {
  313. if (this['need' + p]) {
  314. doCommand(p);
  315. }
  316. }
  317. task('buildPackage', compressTaskArr, function () {});
  318. directory(this.packageDir);
  319. file(packageDirPath, this.packageFiles, function () {
  320. jake.mkdirP(packageDirPath);
  321. let fileList = [];
  322. self.packageFiles.forEach(function (name) {
  323. let f = path.join(self.packageDirPath(), name);
  324. let fDir = path.dirname(f);
  325. jake.mkdirP(fDir, {silent: true});
  326. // Add both files and directories
  327. fileList.push({
  328. from: name,
  329. to: f
  330. });
  331. });
  332. let _copyFile = function () {
  333. let file = fileList.pop();
  334. let stat;
  335. if (file) {
  336. stat = fs.statSync(file.from);
  337. // Target is a directory, just create it
  338. if (stat.isDirectory()) {
  339. jake.mkdirP(file.to, {silent: true});
  340. _copyFile();
  341. }
  342. // Otherwise copy the file
  343. else {
  344. jake.cpR(file.from, file.to, {silent: true});
  345. _copyFile();
  346. }
  347. }
  348. else {
  349. complete();
  350. }
  351. };
  352. _copyFile();
  353. }, {async: true});
  354. };
  355. this.packageName = function () {
  356. if (this.version) {
  357. return this.name + '-' + this.version;
  358. }
  359. else {
  360. return this.name;
  361. }
  362. };
  363. this.packageDirPath = function () {
  364. return this.packageDir + '/' + this.packageName();
  365. };
  366. })();
  367. jake.PackageTask = PackageTask;
  368. exports.PackageTask = PackageTask;