blob: 81f21e54ad4ec84d80bf8d11227e6b409107484f [file] [log] [blame] [raw]
Matt Godboltb6e9a462017-01-02 10:14:46 -06001// Copyright (c) 2012-2017, Matt Godbolt
Matt Godboltbc5848f2016-11-16 21:52:05 -06002// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// * Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9// * Redistributions in binary form must reproduce the above copyright
10// notice, this list of conditions and the following disclaimer in the
11// documentation and/or other materials provided with the distribution.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23// POSSIBILITY OF SUCH DAMAGE.
24
Matt Godboltcf407a82017-11-21 22:14:30 -060025const LRU = require('lru-cache'),
Matt Godboltbc5848f2016-11-16 21:52:05 -060026 Promise = require('promise'), // jshint ignore:line
27 Queue = require('promise-queue'),
28 child_process = require('child_process'),
29 logger = require('./logger').logger,
Matt Godboltcf407a82017-11-21 22:14:30 -060030 _ = require('underscore-node'),
31 Raven = require('raven');
Matt Godboltbc5848f2016-11-16 21:52:05 -060032
33Queue.configure(Promise);
34
Matt Godbolt7990e422016-12-19 18:01:16 -060035function CompilationEnvironment(gccProps, compilerProps) {
36 this.gccProps = gccProps;
37 this.compilerProps = compilerProps;
Matt Godboltbc5848f2016-11-16 21:52:05 -060038 this.okOptions = new RegExp(gccProps('optionsWhitelistRe', '.*'));
39 this.badOptions = new RegExp(gccProps('optionsBlacklistRe', '(?!)'));
40 this.cache = LRU({
41 max: gccProps('cacheMb') * 1024 * 1024,
42 length: function (n) {
43 return JSON.stringify(n).length;
44 }
45 });
46 this.cacheHits = 0;
47 this.cacheMisses = 0;
48 this.compileQueue = new Queue(gccProps("maxConcurrentCompiles", 1), Infinity);
49 this.multiarch = null;
50 try {
51 var multi = child_process.execSync("gcc -print-multiarch").toString().trim();
52 if (multi) {
53 logger.info("Multiarch: " + multi);
54 this.multiarch = multi;
55 } else {
56 logger.info("No multiarch");
57 }
58 } catch (err) {
59 logger.warn("Unable to get multiarch: " + err);
60 }
61}
62
63CompilationEnvironment.prototype.getEnv = function (needsMulti) {
64 var env = {
Matt Godbolt89d71b32017-09-21 16:23:36 -050065 LD_LIBRARY_PATH: process.env.LD_LIBRARY_PATH,
Simon Brand8dbd5dc2017-06-08 19:52:50 +010066 PATH: process.env.PATH,
67 HOME: process.env.HOME
Matt Godboltbc5848f2016-11-16 21:52:05 -060068 };
69 if (needsMulti && this.multiarch) {
70 env.LIBRARY_PATH = '/usr/lib/' + this.multiarch;
71 env.C_INCLUDE_PATH = '/usr/include/' + this.multiarch;
72 env.CPLUS_INCLUDE_PATH = '/usr/include/' + this.multiarch;
73 }
74 return env;
75};
76
77CompilationEnvironment.prototype.cacheStats = function () {
78 var total = this.cacheHits + this.cacheMisses;
79 if ((total % 100) == 1) {
80 var pc = (100 * this.cacheHits) / total;
81 logger.info("Cache stats: " + this.cacheHits + " hits, " + this.cacheMisses + " misses (" + pc.toFixed(2) +
82 "%), LRU has " + this.cache.itemCount + " item(s) totalling " + this.cache.length + " bytes");
83 }
84};
85
86CompilationEnvironment.prototype.cacheGet = function (key) {
87 var cached = this.cache.get(key);
88 if (cached) {
89 this.cacheStats();
90 this.cacheHits++;
91 return cached;
92 }
93 this.cacheMisses++;
94 return undefined;
95};
96
97CompilationEnvironment.prototype.cachePut = function (key, result) {
98 this.cache.set(key, result);
99 this.cacheStats();
100};
101
102CompilationEnvironment.prototype.enqueue = function (job) {
Rubénd1937392017-10-02 21:40:58 +0200103 var wrappedJob = function () {
Matt Godboltd7ac3862017-09-21 15:45:25 -0500104 try {
105 return job();
106 } catch (e) {
Matt Godboltcf407a82017-11-21 22:14:30 -0600107 Raven.captureException(e);
Matt Godboltd7ac3862017-09-21 15:45:25 -0500108 logger.error("Caught promise exception " + e);
109 if (e.stack) logger.info(e.stack);
110 return Promise.resolve(null);
111 }
112 };
113 return this.compileQueue.add(wrappedJob);
Matt Godboltbc5848f2016-11-16 21:52:05 -0600114};
115
Matt Godbolt47195c22017-01-14 23:39:19 -0600116CompilationEnvironment.prototype.isBusy = function () {
117 return this.compileQueue.getPendingLength() > 0 || this.compileQueue.getQueueLength() > 0;
118};
119
Matt Godboltbc5848f2016-11-16 21:52:05 -0600120CompilationEnvironment.prototype.findBadOptions = function (options) {
121 return _.filter(options, function (option) {
122 return !option.match(this.okOptions) || option.match(this.badOptions);
123 }, this);
124};
125
Simon Brand8dbd5dc2017-06-08 19:52:50 +0100126exports.CompilationEnvironment = CompilationEnvironment;