api.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*
  2. * Jake JavaScript build tool
  3. * Copyright 2112 Matthew Eernisse ([email protected])
  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. var api = new (function () {
  19. /**
  20. @name task
  21. @static
  22. @function
  23. @description Creates a Jake Task
  24. `
  25. @param {String} name The name of the Task
  26. @param {Array} [prereqs] Prerequisites to be run before this task
  27. @param {Function} [action] The action to perform for this task
  28. @param {Object} [opts]
  29. @param {Boolean} [opts.asyc=false] Perform this task asynchronously.
  30. If you flag a task with this option, you must call the global
  31. `complete` method inside the task's action, for execution to proceed
  32. to the next task.
  33. @example
  34. desc('This is the default task.');
  35. task('default', function (params) {
  36. console.log('This is the default task.');
  37. });
  38. desc('This task has prerequisites.');
  39. task('hasPrereqs', ['foo', 'bar', 'baz'], function (params) {
  40. console.log('Ran some prereqs first.');
  41. });
  42. desc('This is an asynchronous task.');
  43. task('asyncTask', function () {
  44. setTimeout(complete, 1000);
  45. }, {async: true});
  46. */
  47. this.task = function (name, prereqs, action, opts) {
  48. var args = Array.prototype.slice.call(arguments)
  49. , type
  50. , createdTask;
  51. args.unshift('task');
  52. createdTask = jake.createTask.apply(global, args);
  53. jake.currentTaskDescription = null;
  54. return createdTask;
  55. };
  56. /**
  57. @name rule
  58. @static
  59. @function
  60. @description Creates a Jake Suffix Rule
  61. `
  62. @param {String} pattern The suffix name of the objective
  63. @param {String} source The suffix name of the objective
  64. @param {Array} [prereqs] Prerequisites to be run before this task
  65. @param {Function} [action] The action to perform for this task
  66. @param {Object} [opts]
  67. @param {Boolean} [opts.asyc=false] Perform this task asynchronously.
  68. If you flag a task with this option, you must call the global
  69. `complete` method inside the task's action, for execution to proceed
  70. to the next task.
  71. @example
  72. desc('This is a rule, which does not support namespace or pattern.');
  73. rule('.o', '.c', {async: true}, function () {
  74. var cmd = util.format('gcc -o %s %s', this.name, this.source);
  75. jake.exec([cmd], function () {
  76. complete();
  77. }, {printStdout: true});
  78. });
  79. desc('This rule has prerequisites.');
  80. rule('.o', '.c', ['util.h'], {async: true}, function () {
  81. var cmd = util.format('gcc -o %s %s', this.name, this.source);
  82. jake.exec([cmd], function () {
  83. complete();
  84. }, {printStdout: true});
  85. });
  86. desc('This is a rule with patterns.');
  87. rule('%.o', '%.c', {async: true}, function () {
  88. var cmd = util.format('gcc -o %s %s', this.name, this.source);
  89. jake.exec([cmd], function () {
  90. complete();
  91. }, {printStdout: true});
  92. });
  93. desc('This is another rule with patterns.');
  94. rule('obj/%.o', 'src/%.c', {async: true}, function () {
  95. var cmd = util.format('gcc -o %s %s', this.name, this.source);
  96. jake.exec([cmd], function () {
  97. complete();
  98. }, {printStdout: true});
  99. });
  100. desc('This is an example with chain rules.');
  101. rule('%.pdf', '%.dvi', {async: true}, function () {
  102. var cmd = util.format('dvipdfm %s',this.source);
  103. jake.exec([cmd], function () {
  104. complete();
  105. }, {printStdout: true});
  106. });
  107. rule('%.dvi', '%.tex', {async: true}, function () {
  108. var cmd = util.format('latex %s',this.source);
  109. jake.exec([cmd], function () {
  110. complete();
  111. }, {printStdout: true});
  112. });
  113. desc('This rule has a namespace.');
  114. task('default', ['debug:obj/main.o]);
  115. namespace('debug', {async: true}, function() {
  116. rule('obj/%.o', 'src/%.c', function () {
  117. // ...
  118. });
  119. }
  120. */
  121. this.rule = function () {
  122. var args = Array.prototype.slice.call(arguments)
  123. , arg
  124. , pattern = args.shift()
  125. , source = args.shift()
  126. , prereqs = []
  127. , action = function () {}
  128. , opts = {}
  129. , key = pattern.toString(); // May be a RegExp
  130. while ((arg = args.shift())) {
  131. if (typeof arg == 'function') {
  132. action = arg;
  133. }
  134. else if (Array.isArray(arg)) {
  135. prereqs = arg;
  136. }
  137. else {
  138. opts = arg;
  139. }
  140. }
  141. jake.currentNamespace.rules[key] = new jake.Rule({
  142. pattern: pattern
  143. , source: source
  144. , prereqs: prereqs
  145. , action: action
  146. , opts: opts
  147. , desc: jake.currentTaskDescription
  148. , ns: jake.currentNamespace
  149. });
  150. jake.currentTaskDescription = null;
  151. };
  152. /**
  153. @name directory
  154. @static
  155. @function
  156. @description Creates a Jake DirectoryTask. Can be used as a prerequisite
  157. for FileTasks, or for simply ensuring a directory exists for use with a
  158. Task's action.
  159. `
  160. @param {String} name The name of the DiretoryTask
  161. @example
  162. // Creates the package directory for distribution
  163. directory('pkg');
  164. */
  165. this.directory = function (name) {
  166. var args = Array.prototype.slice.call(arguments)
  167. , createdTask;
  168. args.unshift('directory');
  169. createdTask = jake.createTask.apply(global, args);
  170. jake.currentTaskDescription = null;
  171. return createdTask;
  172. };
  173. /**
  174. @name file
  175. @static
  176. @function
  177. @description Creates a Jake FileTask.
  178. `
  179. @param {String} name The name of the FileTask
  180. @param {Array} [prereqs] Prerequisites to be run before this task
  181. @param {Function} [action] The action to create this file, if it doesn't
  182. exist already.
  183. @param {Object} [opts]
  184. @param {Array} [opts.asyc=false] Perform this task asynchronously.
  185. If you flag a task with this option, you must call the global
  186. `complete` method inside the task's action, for execution to proceed
  187. to the next task.
  188. */
  189. this.file = function (name, prereqs, action, opts) {
  190. var args = Array.prototype.slice.call(arguments)
  191. , createdTask;
  192. args.unshift('file');
  193. createdTask = jake.createTask.apply(global, args);
  194. jake.currentTaskDescription = null;
  195. return createdTask;
  196. };
  197. /**
  198. @name desc
  199. @static
  200. @function
  201. @description Creates a description for a Jake Task (or FileTask,
  202. DirectoryTask). When invoked, the description that iscreated will
  203. be associated with whatever Task is created next.
  204. `
  205. @param {String} description The description for the Task
  206. */
  207. this.desc = function (description) {
  208. jake.currentTaskDescription = description;
  209. };
  210. /**
  211. @name namespace
  212. @static
  213. @function
  214. @description Creates a namespace which allows logical grouping
  215. of tasks, and prevents name-collisions with task-names. Namespaces
  216. can be nested inside of other namespaces.
  217. `
  218. @param {String} name The name of the namespace
  219. @param {Function} scope The enclosing scope for the namespaced tasks
  220. @example
  221. namespace('doc', function () {
  222. task('generate', ['doc:clobber'], function () {
  223. // Generate some docs
  224. });
  225. task('clobber', function () {
  226. // Clobber the doc directory first
  227. });
  228. });
  229. */
  230. this.namespace = function (name, closure) {
  231. var curr = jake.currentNamespace
  232. , ns = curr.childNamespaces[name] || new jake.Namespace(name, curr);
  233. curr.childNamespaces[name] = ns;
  234. jake.currentNamespace = ns;
  235. closure();
  236. jake.currentNamespace = curr;
  237. jake.currentTaskDescription = null;
  238. return ns;
  239. };
  240. /**
  241. @name complete
  242. @static
  243. @function
  244. @description Completes an asynchronous task, allowing Jake's
  245. execution to proceed to the next task. Calling complete globally or without
  246. arguments completes the last task on the invocationChain. If you use parallel
  247. execution of prereqs this will probably complete a wrong task. You should call this
  248. function with this task as the first argument, before the optional return value.
  249. Alternatively you can call task.complete()
  250. `
  251. @example
  252. task('generate', ['doc:clobber'], function () {
  253. exec('./generate_docs.sh', function (err, stdout, stderr) {
  254. if (err || stderr) {
  255. fail(err || stderr);
  256. }
  257. else {
  258. console.log(stdout);
  259. complete();
  260. }
  261. });
  262. }, {async: true});
  263. */
  264. this.complete = function (task, val) {
  265. //this should detect if the first arg is a task, but I guess it should be more thorough
  266. if(task && task. _currentPrereqIndex >=0 ) {
  267. task.complete(val);
  268. } else {
  269. val = task;
  270. if(jake._invocationChain.length > 0) {
  271. jake._invocationChain[jake._invocationChain.length-1].complete(val);
  272. } else {
  273. }
  274. }
  275. };
  276. /**
  277. @name fail
  278. @static
  279. @function
  280. @description Causes Jake execution to abort with an error.
  281. Allows passing an optional error code, which will be used to
  282. set the exit-code of exiting process.
  283. `
  284. @param {Error|String} err The error to thow when aborting execution.
  285. If this argument is an Error object, it will simply be thrown. If
  286. a String, it will be used as the error-message. (If it is a multi-line
  287. String, the first line will be used as the Error message, and the
  288. remaining lines will be used as the error-stack.)
  289. @example
  290. task('createTests, function () {
  291. if (!fs.existsSync('./tests')) {
  292. fail('Test directory does not exist.');
  293. }
  294. else {
  295. // Do some testing stuff ...
  296. }
  297. });
  298. */
  299. this.fail = function (err, code) {
  300. var msg
  301. , errObj;
  302. if (code) {
  303. jake.errorCode = code;
  304. }
  305. if (err) {
  306. if (typeof err == 'string') {
  307. // Use the initial or only line of the error as the error-message
  308. // If there was a multi-line error, use the rest as the stack
  309. msg = err.split('/n');
  310. errObj = new Error(msg.shift());
  311. if (msg.length) {
  312. errObj.stack = msg.join('\n');
  313. }
  314. throw errObj;
  315. }
  316. else if (err instanceof Error) {
  317. throw err;
  318. }
  319. else {
  320. throw new Error(err.toString());
  321. }
  322. }
  323. else {
  324. throw new Error();
  325. }
  326. };
  327. this.packageTask = function (name, version, definition) {
  328. return new jake.PackageTask(name, version, definition);
  329. };
  330. this.publishTask = function (name, prereqs, opts, definition) {
  331. return new jake.PublishTask(name, prereqs, opts, definition);
  332. };
  333. // Backward-compat
  334. this.npmPublishTask = function (name, prereqs, opts, definition) {
  335. return new jake.PublishTask(name, prereqs, opts, definition);
  336. };
  337. this.watchTask = function (name, taskNames, definition) {
  338. return new jake.WatchTask(name, taskNames, definition);
  339. };
  340. this.testTask = function () {
  341. var ctor = function () {}
  342. , t;
  343. ctor.prototype = jake.TestTask.prototype;
  344. t = new ctor();
  345. jake.TestTask.apply(t, arguments);
  346. return t;
  347. };
  348. })();
  349. module.exports = api;