<commit_before>'use strict';

var Q           = require('q'),
    COLORS      = require('colors'),
    BENCHMARK   = require('benchmark'),
    FS          = require('fs'),
    VM          = require('vm'),
    PATH        = require('path'),
    CP          = require('child_process'),
    INHERIT     = require('inherit'),
    LOGGER      = require('bem').logger,
    CREATE_LEVEL= require('bem').createLevel,
    U           = require('bem').util,

    Table       = require('cli-table');

/**
 * Benchmark - testing on BEMHTML templates performance between revisions
 */
var Benchmark = INHERIT({

    __constructor: function(opts, args){

        this.pathTmp        = '.bem/cache/bench/';
        this.pathSelf       = 'current_state';
        this.pathBenchmarks = 'benchmark.bundles';
        this.treeishList    = args['treeish-list'] || [];
        this.withoutCurrent = opts['no-wc'];
        this.rerun          = opts.rerun;
        this.benchmarks     = opts.benchmarks;
        this.techs          = opts.techs;

        this.DELAY             = opts.delay ? opts.delay : 20;
        this.WARMING_CYCLE     = 100;
        this.ALLOWABLE_PERCENT = opts.rme ? opts.rme : 5;

    },

    /**
     * Extract one revision to pathTmp folder
     *
     * @param  {String} treeish  Hash, HEAD, tag...
     * @return {Promise * String}  Path to temporary revision
     */
    exportGitRevision: function(treeish) {

        var def = Q.defer(),
            label = 'Export git revision [' + treeish.green + ']',
            archiveErr = '',
            extractErr = '',
            extract,
            archive;

        LOGGER.time(label);

        archive = CP.spawn('git', [
            'archive',
            '--format=tar',
            '--prefix=' + this.pathTmp + '/' + treeish + '/',
            treeish
        ]);
        extract = CP.spawn('tar', ['-x']);

        archive.stdout.on('data', function(data) {
            extract.stdin.write(data);
        });

        archive.stderr.on('data', function(err) {
            archiveErr += err.toString().replace(/(\n)$/, '');
        });

        archive.on('close', function(code) {
            if (code !== 0) {
                LOGGER.error('exportGitRevision - '.blue + '[' + treeish.green + '] ' + archiveErr);
                def.reject(new Error(archiveErr));
            } else {
                LOGGER.timeEnd(label);
                def.resolve(treeish);
            }
            extract.stdin.end();
        });

        extract.stderr.on('data', function(err) {
            extractErr += err.toString().replace(/(\n)$/, '');
        });

        extract.on('close', function(code) {
            if (code !== 0) {
                LOGGER.error('exportGitRevision - '.blue + '[' + treeish.green + '] ' + extractErr);
                def.reject(new Error(extractErr));
            }
        });

        return def.promise;

    },

    /**
     * Copy current files state with exclude
     *
     * @return {Promise * String}  Path to files
     */
    exportWorkingCopy: function() {

        var self = this,
            label = 'Export working copy [' + self.pathSelf.green + ']',
            cmd = [
                'rsync',
                '-a',
                '--exclude=/.git',
                '--exclude=/.svn',
                '--exclude=/' + self.pathTmp,
                '.',
                self.pathTmp + self.pathSelf
            ].join(' ');

        LOGGER.time(label);

        return U.exec(cmd)
            .fail(function(err) {
                LOGGER.error('exportWorkingCopy'.blue);
                return Q.reject(err);
            })
            .then(function() {
                LOGGER.timeEnd(label);
                return self.pathSelf;
            });

    },

    /**
     *  Getting all paths to .bemjson.js file on needed level
     *
     *  @param  {String} treeishPath  Checked out treeish path
     *  @param  {String} levelPath  level path
     *  @return {Array}  Array of links
     */
    getBenchmarksPaths: function(treeishPath, levelPath) {

        var self = this,
            level =  CREATE_LEVEL(PATH.join(treeishPath, levelPath)),
            benchmarks;

        if (self.benchmarks && self.benchmarks.length) {
            // if -b flag detected then compare not all benchmarks
            benchmarks = U.arrayUnique(self.benchmarks)
                .map(function(b) {
                    return U.bemParseKey(b);
                });
        }
        else {
            // if -b flag not detected then all benchmarks
            benchmarks = level.getItemsByIntrospection()
                .filter(function(item) {
                    return item.tech === 'bemjson.js';
                });
        }

        return benchmarks
            .map(function(benchmark) {
                return level.getPathByObj(benchmark, 'bemjson.js');
            });

    },

    /**
     * Make one target and return all links on blocks
     *
     * @param  {String} target  Path to target
     * @return {Array}  All links of target
     */
    makeTarget: function(target) {

        var self = this,
            targets = [self.pathBenchmarks],
            label = '[' + target.green + '] has been assembled';

        LOGGER.time(label);

        if (self.benchmarks && self.benchmarks.length) {
            targets = self.benchmarks
                .map(function(b) {
                    return PATH.join(self.pathBenchmarks, b);
                });
        }

        return U.exec('cd ' + PATH.join(self.pathTmp, target) + ' && npm install && ./node_modules/.bin/bem make ' + targets.join(' '))
            .fail(function(err) {
                LOGGER.error(target.red + ' not make');
                return Q.reject(err);
            })
            .then(function() {
                LOGGER.timeEnd(label);
            });

    },

    /**
     * Make all targets and run benchmarks for each target
     *
     * @param  {Array} targets  List of targets
     * @return {Promise * Undefined}  Object with tests results
     */
    makeAll: function(targets) {
        return Q.all(targets.map(this.makeTarget.bind(this)))
            .then(function() {});<commit_msg>Add realization of a make customization<commit_after>'use strict';

var Q           = require('q'),
    COLORS      = require('colors'),
    BENCHMARK   = require('benchmark'),
    FS          = require('fs'),
    VM          = require('vm'),
    PATH        = require('path'),
    CP          = require('child_process'),
    INHERIT     = require('inherit'),
    LOGGER      = require('bem').logger,
    CREATE_LEVEL= require('bem').createLevel,
    U           = require('bem').util,

    Table       = require('cli-table');

/**
 * Benchmark - testing on BEMHTML templates performance between revisions
 */
var Benchmark = INHERIT({

    __constructor: function(opts, args){

        this.pathTmp        = '.bem/cache/bench/';
        this.pathSelf       = 'current_state';
        this.pathBenchmarks = 'benchmark.bundles';
        this.treeishList    = args['treeish-list'] || [];
        this.withoutCurrent = opts['no-wc'];
        this.rerun          = opts.rerun;
        this.benchmarks     = opts.benchmarks;
        this.techs          = opts.techs;

        this.DELAY             = opts.delay ? opts.delay : 20;
        this.WARMING_CYCLE     = 100;
        this.ALLOWABLE_PERCENT = opts.rme ? opts.rme : 5;

    },

    /**
     * Extract one revision to pathTmp folder
     *
     * @param  {String} treeish  Hash, HEAD, tag...
     * @return {Promise * String}  Path to temporary revision
     */
    exportGitRevision: function(treeish) {

        var def = Q.defer(),
            label = 'Export git revision [' + treeish.green + ']',
            archiveErr = '',
            extractErr = '',
            extract,
            archive;

        LOGGER.time(label);

        archive = CP.spawn('git', [
            'archive',
            '--format=tar',
            '--prefix=' + this.pathTmp + '/' + treeish + '/',
            treeish
        ]);
        extract = CP.spawn('tar', ['-x']);

        archive.stdout.on('data', function(data) {
            extract.stdin.write(data);
        });

        archive.stderr.on('data', function(err) {
            archiveErr += err.toString().replace(/(\n)$/, '');
        });

        archive.on('close', function(code) {
            if (code !== 0) {
                LOGGER.error('exportGitRevision - '.blue + '[' + treeish.green + '] ' + archiveErr);
                def.reject(new Error(archiveErr));
            } else {
                LOGGER.timeEnd(label);
                def.resolve(treeish);
            }
            extract.stdin.end();
        });

        extract.stderr.on('data', function(err) {
            extractErr += err.toString().replace(/(\n)$/, '');
        });

        extract.on('close', function(code) {
            if (code !== 0) {
                LOGGER.error('exportGitRevision - '.blue + '[' + treeish.green + '] ' + extractErr);
                def.reject(new Error(extractErr));
            }
        });

        return def.promise;

    },

    /**
     * Copy current files state with exclude
     *
     * @return {Promise * String}  Path to files
     */
    exportWorkingCopy: function() {

        var self = this,
            label = 'Export working copy [' + self.pathSelf.green + ']',
            cmd = [
                'rsync',
                '-a',
                '--exclude=/.git',
                '--exclude=/.svn',
                '--exclude=/' + self.pathTmp,
                '.',
                self.pathTmp + self.pathSelf
            ].join(' ');

        LOGGER.time(label);

        return U.exec(cmd)
            .fail(function(err) {
                LOGGER.error('exportWorkingCopy'.blue);
                return Q.reject(err);
            })
            .then(function() {
                LOGGER.timeEnd(label);
                return self.pathSelf;
            });

    },

    /**
     *  Getting all paths to .bemjson.js file on needed level
     *
     *  @param  {String} treeishPath  Checked out treeish path
     *  @param  {String} levelPath  level path
     *  @return {Array}  Array of links
     */
    getBenchmarksPaths: function(treeishPath, levelPath) {

        var self = this,
            level =  CREATE_LEVEL(PATH.join(treeishPath, levelPath)),
            benchmarks;

        if (self.benchmarks && self.benchmarks.length) {
            // if -b flag detected then compare not all benchmarks
            benchmarks = U.arrayUnique(self.benchmarks)
                .map(function(b) {
                    return U.bemParseKey(b);
                });
        }
        else {
            // if -b flag not detected then all benchmarks
            benchmarks = level.getItemsByIntrospection()
                .filter(function(item) {
                    return item.tech === 'bemjson.js';
                });
        }

        return benchmarks
            .map(function(benchmark) {
                return level.getPathByObj(benchmark, 'bemjson.js');
            });

    },

    /**
     * Make one target and return all links on blocks
     *
     * @param  {String} target  Path to target
     * @return {Array}  All links of target
     */
    makeTarget: function(target) {

        var self = this,
            targets = [self.pathBenchmarks],
            label = '[' + target.green + '] has been assembled',
            packageJson = FS.readFileSync(PATH.join(self.pathTmp, target, 'package.json')).toString(),
            script = JSON.parse(packageJson).scripts,
            defaultMake = 'npm install && ./node_modules/.bin/bem make',
            make = null;

        LOGGER.time(label);

        if (self.benchmarks && self.benchmarks.length) {
            targets = self.benchmarks
                .map(function(b) {
                    return PATH.join(self.pathBenchmarks, b);
                });
        }

        if(script && script['bem-bench-build']) {
            make = script['bem-bench-build'];
            if (/\$targets/.test(make)) {
                if (!self.benchmarks) {
                    LOGGER.warn('$targets'.blue + ' not been defined through ' + "-b".green + ' option');
                    LOGGER.warn('$targets'.blue + ' change to ' + self.pathBenchmarks.blue);
                }
                make = make.replace(/\$targets/, targets.join(' '));
            }
        } else {
            make = defaultMake + ' ' + targets.join(' ');
        }

        LOGGER.info('MAKE STRING:\n', make.replace(/&&/gi, '&&\n'.green).underline);

        return U.exec('cd ' + PATH.join(self.pathTmp, target) + ' && ' + make)
            .fail(function(err) {
                LOGGER.error(target.red + ' not make');
                return Q.reject(err);
            })
            .then(function() {
                LOGGER.timeEnd(label);
            });

    },

    /**
     * Make all targets and run benchmarks for each target
     *
     * @param  {Array} targets  List of targets
     * @return {Promise * Undefined}  Object with tests results
     */
    makeAll: function(targets) {
        return Q.all(targets.map(this.makeTarget.bind(this)))
            .then(function() {});
{'fraction_non_alphanumeric': 0.1391448859344312, 'fraction_numerical': 0.0012086417887898473, 'mean_word_length': 1.5164417411138567, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2519230', 'n_tokens_mistral': 3879, 'n_tokens_neox': 3682, 'n_words': 754}