5 * Read dependency info from a file, and get dependency tree/library load order
6 * from the dependency info of all files
8 * ***************************************************************************
9 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
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:
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
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 * ***************************************************************************
30 * Authors: Youmin Ha <youmin.ha@samsung.com>
33 var util = require("util"),
34 assert = require("assert"),
35 path = require("path"),
38 child_process = require('child_process'),
43 path.sep = path.sep ? path.sep : '/';
45 function DepInfo( fPath, basedir ) {
53 function define( depModList, moduleBody ) {
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' ) );
63 origDir = path.resolve(); // Backup origDir
65 if ( ! basedir || ! basedir.length ) {
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' ) );
75 self.description = '';
78 code = fs.readFileSync( self.path, 'utf-8' );
79 linebuf = code.split( /\n\r?/ );
80 for( var idx in linebuf ) {
82 if( line.match( /^\/\/>>label:/ ) ) {
83 self.label = line.replace( /^\/\/>>label:(.*)$/, "$1" ).trim();
85 if( line.match( /^\/\/>>group:/ ) ) {
86 self.group = line.replace( /^\/\/>>group:(.*)$/, "$1" ).trim();
88 if( line.match( /^\/\/>>description:/ ) ) {
89 self.description = line.replace( /^\/\/>>description:(.*)$/, "$1" ).trim();
92 context = vm.createContext( { define: define } );
93 vm.runInContext( code, context ); // Set depends
99 process.chdir( origDir ); // Back to origDir
102 DepInfo.prototype = {
103 fullPath: function ( ) {
104 return path.normalize( path.join( this.basedir, this.path ) );
106 filePath: function ( ) {
107 return path.relative( this.basedir, this.fullPath() );
111 function DepTree( ) {
114 DepTree.prototype = {
118 setBasedir: function ( ) {
119 this.basedir = arguments[0];
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 ) {
128 di = new DepInfo( fPath, this.basedir );
130 this._depData[ di.name ] = di;
136 depData: function ( ) {
137 return this._depData;
139 order: function ( reqList ) {
144 mName, // current module name
145 mDep, // current module's dependency (array)
150 reqList = Object.keys( self._depData );
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 ) {
160 depData[ modName ][i] = self._depData[ modName ].depends[i];
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] );
171 while( pool.length > 0 ) {
172 // Sort pool with remained dependency
173 pool.sort( function ( a, b ) {
175 la = depData[ a ] ? depData[ a ].length : 0;
176 lb = depData[ b ] ? depData[ b ].length : 0;
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.
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
195 construct: function ( ) {
196 var dd = this._depData,
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.
209 import: function ( depData ) {
211 this._depData = depData;
213 printDepJSON: function ( ) {
214 puts( JSON.stringify( this._depData, null, '\t' ) );
219 module.exports.DepInfo = DepInfo;
220 module.exports.DepTree = DepTree;
223 function main( argv ) {
224 var option = argv[2],
229 createDepInfo( val );
232 calculateDepOrder( val, argv[4], argv[5] );
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" );
245 function createDepInfo( basedir ) {
247 origdir = path.resolve(); // Remember original dir
249 if( ! basedir ) basedir = '.';
252 dt.setBasedir( basedir );
254 child_process.exec('find ' + basedir + ' -name "*.js" -type f',
255 function ( err, stdout, stderr ) {
261 error( 'Failure finding js files in ' + basedir );
262 process.chdir( origdir );
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$/ ) ) {
279 function calculateDepOrder( modPath, depDataPath, requirePath ) {
280 var dt = new DepTree(),
289 function _require( depList ) {
291 for( i in depList ) {
292 //dt.add( depList[i] );
293 require.push( depList[i] );
297 dt.setBasedir( modPath );
299 if( path.basename( depDataPath ) == "depData.json" ) {
300 code = fs.readFileSync( depDataPath, 'utf-8' );
301 depData = JSON.parse( code );
302 dt.import( depData );
304 if ( path.basename( requirePath ) == "require.js" ) {
306 code = fs.readFileSync( requirePath, 'utf-8' );
307 context = vm.createContext( { require: _require } );
308 vm.runInContext( code, context );
312 order = dt.order( require );
319 code = fs.readFileSync( path.join( modPath, mod + '.js' ) );
322 if( ex.code != "ENOENT" ) { // Ignore No entry(no file) error
329 if ( require.main === module ) main( process.argv );