[Service] Upgrade device home as v1.0.1
[platform/framework/web/wrtjs.git] / device_home / node_modules / jake / lib / rule.js
1 let path = require('path');
2 let fs = require('fs');
3 let Task = require('./task/task').Task;
4
5 // Split a task to two parts, name space and task name.
6 // For example, given 'foo:bin/a%.c', return an object with
7 // - 'ns'     : foo
8 // - 'name'   : bin/a%.c
9 function splitNs(task) {
10   let parts = task.split(':');
11   let name = parts.pop();
12   let ns = resolveNs(parts);
13   return {
14     'name' : name,
15     'ns'   : ns
16   };
17 }
18
19 // Return the namespace based on an array of names.
20 // For example, given ['foo', 'baz' ], return the namespace
21 //
22 //   default -> foo -> baz
23 //
24 // where default is the global root namespace
25 // and -> means child namespace.
26 function resolveNs(parts) {
27   let  ns = jake.defaultNamespace;
28   for(let i = 0, l = parts.length; ns && i < l; i++) {
29     ns = ns.childNamespaces[parts[i]];
30   }
31   return ns;
32 }
33
34 // Given a pattern p, say 'foo:bin/a%.c'
35 // Return an object with
36 // - 'ns'     : foo
37 // - 'dir'    : bin
38 // - 'prefix' : a
39 // - 'suffix' : .c
40 function resolve(p) {
41   let task = splitNs(p);
42   let name  = task.name;
43   let ns    = task.ns;
44   let split = path.basename(name).split('%');
45   return {
46     ns: ns,
47     dir: path.dirname(name),
48     prefix: split[0],
49     suffix: split[1]
50   };
51 }
52
53 // Test whether string a is a suffix of string b
54 function stringEndWith(a, b) {
55   let l;
56   return (l = b.lastIndexOf(a)) == -1 ? false : l + a.length == b.length;
57 }
58
59 // Replace the suffix a of the string s with b.
60 // Note that, it is assumed a is a suffix of s.
61 function stringReplaceSuffix(s, a, b) {
62   return s.slice(0, s.lastIndexOf(a)) + b;
63 }
64
65 class Rule {
66   constructor(opts) {
67     this.pattern = opts.pattern;
68     this.source = opts.source;
69     this.prereqs = opts.prereqs;
70     this.action = opts.action;
71     this.opts = opts.opts;
72     this.desc =  opts.desc;
73     this.ns = opts.ns;
74   }
75
76   // Create a file task based on this rule for the specified
77   // task-name
78   // ======
79   // FIXME: Right now this just throws away any passed-in args
80   // for the synthsized task (taskArgs param)
81   // ======
82   createTask(fullName, level) {
83     let self = this;
84     let pattern;
85     let source;
86     let action;
87     let opts;
88     let prereqs;
89     let valid;
90     let src;
91     let tNs;
92     let createdTask;
93     let name = Task.getBaseTaskName(fullName);
94     let nsPath = Task.getBaseNamespacePath(fullName);
95     let ns = this.ns.resolveNamespace(nsPath);
96
97     pattern = this.pattern;
98     source = this.source;
99
100     if (typeof source == 'string') {
101       src = Rule.getSource(name, pattern, source);
102     }
103     else {
104       src = source(name);
105     }
106
107     // TODO: Write a utility function that appends a
108     // taskname to a namespace path
109     src = nsPath.split(':').filter(function (item) {
110       return !!item;
111     }).concat(src).join(':');
112
113     // Generate the prerequisite for the matching task.
114     //    It is the original prerequisites plus the prerequisite
115     //    representing source file, i.e.,
116     //
117     //      rule( '%.o', '%.c', ['some.h'] ...
118     //
119     //    If the objective is main.o, then new task should be
120     //
121     //      file( 'main.o', ['main.c', 'some.h' ] ...
122     prereqs = this.prereqs.slice(); // Get a copy to work with
123     prereqs.unshift(src);
124
125     // Prereq should be:
126     // 1. an existing task
127     // 2. an existing file on disk
128     // 3. a valid rule (i.e., not at too deep a level)
129     valid = prereqs.some(function (p) {
130       let ns = self.ns;
131       return ns.resolveTask(p) ||
132         fs.existsSync(Task.getBaseTaskName(p)) ||
133         jake.attemptRule(p, ns, level + 1);
134     });
135
136     // If any of the prereqs aren't valid, the rule isn't valid
137     if (!valid) {
138       return null;
139     }
140     // Otherwise, hunky-dory, finish creating the task for the rule
141     else {
142       // Create the action for the task
143       action = function () {
144         let task = this;
145         self.action.apply(task);
146       };
147
148       opts = this.opts;
149
150       // Insert the file task into Jake
151       //
152       // Since createTask function stores the task as a child task
153       // of currentNamespace. Here we temporariliy switch the namespace.
154       // FIXME: Should allow optional ns passed in instead of this hack
155       tNs = jake.currentNamespace;
156       jake.currentNamespace = ns;
157       createdTask = jake.createTask('file', name, prereqs, action, opts);
158       createdTask.source = src.split(':').pop();
159       jake.currentNamespace = tNs;
160
161       return createdTask;
162     }
163   }
164
165   match(name) {
166     return Rule.match(this.pattern, name);
167   }
168
169   // Test wether the a prerequisite matchs the pattern.
170   // The arg 'pattern' does not have namespace as prefix.
171   // For example, the following tests are true
172   //
173   //   pattern      |    name
174   //   bin/%.o      |    bin/main.o
175   //   bin/%.o      |    foo:bin/main.o
176   //
177   // The following tests are false (trivally)
178   //
179   //   pattern      |    name
180   //   bin/%.o      |    foobin/main.o
181   //   bin/%.o      |    bin/main.oo
182   static match(pattern, name) {
183     let p;
184     let task;
185     let obj;
186     let filename;
187
188     if (pattern instanceof RegExp) {
189       return pattern.test(name);
190     }
191     else if (pattern.indexOf('%') == -1) {
192       // No Pattern. No Folder. No Namespace.
193       // A Simple Suffix Rule. Just test suffix
194       return stringEndWith(pattern, name);
195     }
196     else {
197       // Resolve the dir, prefix and suffix of pattern
198       p = resolve(pattern);
199
200       // Resolve the namespace and task-name
201       task = splitNs(name);
202       name = task.name;
203
204       // Set the objective as the task-name
205       obj = name;
206
207       // Namespace is already matched.
208
209       // Check dir
210       if (path.dirname(obj) != p.dir) {
211         return false;
212       }
213
214       filename = path.basename(obj);
215
216       // Check file name length
217       if ((p.prefix.length + p.suffix.length + 1) > filename.length) {
218         // Length does not match.
219         return false;
220       }
221
222       // Check prefix
223       if (filename.indexOf(p.prefix) !== 0) {
224         return false;
225       }
226
227       // Check suffix
228       if (!stringEndWith(p.suffix, filename)) {
229         return false;
230       }
231
232       // OK. Find a match.
233       return true;
234     }
235   }
236
237   // Generate the source based on
238   //  - name    name for the synthesized task
239   //  - pattern    pattern for the objective
240   //  - source    pattern for the source
241   //
242   // Return the source with properties
243   //  - dep      the prerequisite of source
244   //             (with the namespace)
245   //
246   //  - file     the file name of source
247   //             (without the namespace)
248   //
249   // For example, given
250   //
251   //  - name   foo:bin/main.o
252   //  - pattern    bin/%.o
253   //  - source    src/%.c
254   //
255   //    return 'foo:src/main.c',
256   //
257   static getSource(name, pattern, source) {
258     let dep;
259     let pat;
260     let match;
261     let file;
262     let src;
263
264     // Regex pattern -- use to look up the extension
265     if (pattern instanceof RegExp) {
266       match = pattern.exec(name);
267       if (match) {
268         if (typeof source == 'function') {
269           src = source(name);
270         }
271         else {
272           src = stringReplaceSuffix(name, match[0], source);
273         }
274       }
275     }
276     // Assume string
277     else {
278       // Simple string suffix replacement
279       if (pattern.indexOf('%') == -1) {
280         if (typeof source == 'function') {
281           src = source(name);
282         }
283         else {
284           src = stringReplaceSuffix(name, pattern, source);
285         }
286       }
287       // Percent-based substitution
288       else {
289         pat = pattern.replace('%', '(.*?)');
290         pat = new RegExp(pat);
291         match = pat.exec(name);
292         if (match) {
293           if (typeof source == 'function') {
294             src = source(name);
295           }
296           else {
297             file = match[1];
298             file = source.replace('%', file);
299             dep = match[0];
300             src = name.replace(dep, file);
301           }
302         }
303       }
304     }
305
306     return src;
307   }
308 }
309
310
311 exports.Rule = Rule;