Merge branch 'master' into tizen_2.1
[platform/framework/web/web-ui-fw.git] / tools / moduledep.js
1 #!/usr/bin/env node
2 /*
3  * moduledep.js
4  *
5  * Read dependency info from a file, and get dependency tree/library load order 
6  * from the dependency info of all files
7  *
8  * ***************************************************************************
9  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  * ***************************************************************************
29  *
30  * Authors: Youmin Ha <youmin.ha@samsung.com>
31 */
32
33 var util = require("util"),
34         assert = require("assert"),
35         path = require("path"),
36         vm = require("vm"),
37         fs = require("fs"),
38         child_process = require('child_process'),
39         puts = util.puts,
40         error = util.error,
41         print = util.print;
42
43 path.sep = path.sep ? path.sep : '/';
44
45 function DepInfo( fPath, basedir ) {
46         var self = this,
47                 code,
48                 context,
49                 origDir,
50                 linebuf,
51                 line;
52
53         function define( depModList, moduleBody ) {
54                 var p, i;
55                 if ( typeof depModList == 'object' ) {
56                         for( i in depModList ) {
57                                 p = path.join( path.dirname( self.filePath() ), depModList[i] );
58                                 self.depends.push( path.normalize( p, '.js' ) );
59                         }
60                 }
61         }
62
63         origDir = path.resolve();       // Backup origDir
64
65         if ( ! basedir || ! basedir.length ) {
66                 basedir = ".";
67         }
68         process.chdir( basedir );       // Move to basedir
69         self.basedir = path.resolve( );         //self.basedir: absolute path.
70         self.path = path.normalize( fPath );
71         self.name = path.join( path.dirname( self.path ), path.basename( self.path, '.js' ) );
72         self.depends = [ ];
73         self.label = '';
74         self.group = '';
75         self.description = '';
76
77         try {
78                 code = fs.readFileSync( self.path, 'utf-8' );
79                 linebuf = code.split( /\n\r?/ );
80                 for( var idx in linebuf ) {
81                         line = linebuf[idx];
82                         if( line.match( /^\/\/>>label:/ ) ) {
83                                 self.label = line.replace( /^\/\/>>label:(.*)$/, "$1" ).trim();
84                         }
85                         if( line.match( /^\/\/>>group:/ ) ) {
86                                 self.group = line.replace( /^\/\/>>group:(.*)$/, "$1" ).trim();
87                         }
88                         if( line.match( /^\/\/>>description:/ ) ) {
89                                 self.description = line.replace( /^\/\/>>description:(.*)$/, "$1" ).trim();
90                         }
91                 }
92                 context = vm.createContext( { define: define } );
93                 vm.runInContext( code, context );       // Set depends
94         } catch ( e ) {
95                 // file read failure
96                 //error(e);
97                 return null;
98         } finally {
99                 process.chdir( origDir );       // Back to origDir
100         }
101 }
102 DepInfo.prototype = {
103         fullPath: function ( ) {
104                 return path.normalize( path.join( this.basedir, this.path ) );
105         },
106         filePath: function ( ) {
107                 return path.relative( this.basedir, this.fullPath() );
108         }
109 }
110
111 function DepTree( ) {
112         return this;
113 }
114 DepTree.prototype = {
115         _depData: { },
116
117         // Set basedir(s)
118         setBasedir: function ( ) {
119                 this.basedir = arguments[0];
120         },
121
122         // Add dependency data from file at the path
123         // @param[in]   fPath   module file's path
124         // @return      true if collection is successful, false if not
125         add: function ( fPath ) {
126                 var di;
127
128                 di = new DepInfo( fPath, this.basedir );
129                 if ( di ) {
130                         this._depData[ di.name ] = di;
131                 } else {
132                         return false;
133                 }
134                 return true;
135         },
136         depData: function ( ) {
137                 return this._depData;
138         },
139         order: function ( reqList ) {
140                 var self = this,
141                         depData = {},
142                         order = [],
143                         pool = [],
144                         mName,  // current module name
145                         mDep,   // current module's dependency (array)
146                         depIdx,
147                         i, j;
148
149                 if( ! reqList ) {
150                         reqList = Object.keys( self._depData );
151                 }
152
153                 // copy _depData into pool & depData
154                 for( j=0; j<reqList.length; j++ ) {
155                         modName = reqList[j];
156                         pool.push( modName );
157                         depData[ modName ] = [];
158                         for( i in self._depData[ modName ].depends ) {
159                                 // deep copy
160                                 depData[ modName ][i] = self._depData[ modName ].depends[i];
161
162                                 // Still not exist in the reqList, add it
163                                 if( reqList.indexOf( depData[ modName ][i] ) == -1 ) {
164                                         //error("NOT found! " + depData[ modName ][i] );
165                                         reqList.push( depData[ modName ][i] );
166                                 }
167                         }
168                 };
169
170                 // Mark and sweep
171                 while( pool.length > 0 ) {
172                         // Sort pool with remained dependency
173                         pool.sort( function ( a, b ) {
174                                 var la, lb;
175                                 la =  depData[ a ] ? depData[ a ].length : 0;
176                                 lb =  depData[ b ] ? depData[ b ].length : 0;
177                                 return la - lb;
178                         } );
179
180                         // Sweep
181                         mName = pool.shift();   // Pop a module name
182                         order.push( mName );    // Add first module to the order (Don't consider whether depData[mName].length == 0.
183
184                         // and Mark
185                         for( i = 0; i < pool.length; i++ ) {    // For all remained modules,
186                                 mDep = depData[ pool[i] ];      // Get a dependency list
187                                 depIdx = mDep.indexOf( mName );
188                                 if ( depIdx != -1 ) {   // Found a dependency!
189                                         mDep.splice( depIdx, 1 );       // Remove mName module
190                                 }
191                         }
192                 }
193                 return order;
194         },
195         construct: function ( ) {
196                 var dd = this._depData,
197                         di,
198                         i, j, m;
199                 for( i in dd ) {
200                         di = dd[i];             // Get DependencyInfo
201                         for( j in di.depends ) {
202                                 m = di.depends[ j ];    // Per dependent module,
203                                 if ( ! dd[ m ] ) {              // if there is no module in depData,
204                                         this.add( m );          // add it.
205                                 }
206                         }
207                 }
208         },
209         import: function ( depData ) {
210                 var i;
211                 this._depData = depData;
212         },
213         printDepJSON: function ( ) {
214                 puts( JSON.stringify( this._depData, null, '\t' ) );
215         }
216 };
217
218 // Export modules
219 module.exports.DepInfo = DepInfo;
220 module.exports.DepTree = DepTree;
221
222 // main module
223 function main( argv ) {
224         var option = argv[2],
225                 val = argv[3];
226
227         switch( option ) {
228                 case '-c':
229                         createDepInfo( val );
230                         break;
231                 case '-d':
232                         calculateDepOrder( val, argv[4], argv[5] );
233                         break;
234                 default:
235                         usage();
236         }
237
238         function usage() {
239                 error("Usage: " + path.basename( argv[1] ) + " [option] [argument]" );
240                 error("       " + path.basename( argv[1] ) + " -c [module directory path] : Create a dependency data" );
241                 error("       " + path.basename( argv[1] ) + " -d [module directory path] [depData.json path] [require file path]: Calculate a dependency order, and make a library" );
242                 process.exit();
243         }
244
245         function createDepInfo( basedir ) {
246                 var dt,
247                         origdir = path.resolve();       // Remember original dir
248
249                 if( ! basedir ) basedir = '.';
250
251                 dt = new DepTree();
252                 dt.setBasedir( basedir );
253
254                 child_process.exec('find ' + basedir +  ' -name "*.js" -type f',
255                         function ( err, stdout, stderr ) {
256                                 var fpathlist,
257                                         fpath,
258                                         i;
259
260                                 if( err ) {
261                                         error( 'Failure finding js files in ' + basedir );
262                                         process.chdir( origdir );
263                                         process.exit(1);
264                                 }
265                                 fpathlist = stdout.split(/\r?\n/);
266                                 for( i in fpathlist ) {
267                                         fpath = fpathlist[i];
268                                         fpath =  path.relative( basedir, fpath );
269                                         //puts(">> " + fpath + " ? " + fpath.match( /.*\.js$/ ) );
270                                         if( fpath.match( /.*\.js$/ ) ) {
271                                                 dt.add( fpath );
272                                         }
273                                 }
274                                 dt.construct();
275                                 dt.printDepJSON();
276                         } );
277         }
278
279         function calculateDepOrder( modPath, depDataPath, requirePath ) {
280                 var dt = new DepTree(),
281                         depData,
282                         require = null,
283                         code,
284                         context,
285                         order,
286                         mod,
287                         i;
288
289                 function _require( depList ) {
290                         var i;
291                         for( i in depList ) {
292                                 //dt.add( depList[i] );
293                                 require.push( depList[i] );
294                         }
295                 }
296
297                 dt.setBasedir( modPath );
298
299                 if( path.basename( depDataPath ) == "depData.json" ) {
300                         code = fs.readFileSync( depDataPath, 'utf-8' );
301                         depData = JSON.parse( code );
302                         dt.import( depData );
303                 }
304                 if ( path.basename( requirePath ) == "require.js" ) {
305                         require = [];
306                         code = fs.readFileSync( requirePath, 'utf-8' );
307                         context = vm.createContext( { require: _require } );
308                         vm.runInContext( code, context );
309                 }
310
311                 dt.construct();
312                 order = dt.order( require );
313                 //error(order);
314
315
316                 for( i in order ) {
317                         mod = order[i];
318                         try {
319                                 code = fs.readFileSync( path.join( modPath, mod + '.js' ) );
320                                 puts(code);
321                         } catch( ex ) {
322                                 if( ex.code != "ENOENT" ) {     // Ignore No entry(no file) error
323                                         throw( ex );
324                                 }
325                         }
326                 }
327         }
328 }
329 if ( require.main === module ) main( process.argv );