build.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*!
  2. * node-sass: scripts/build.js
  3. */
  4. var pkg = require('../package.json'),
  5. fs = require('fs'),
  6. mkdir = require('mkdirp'),
  7. path = require('path'),
  8. spawn = require('cross-spawn'),
  9. sass = require('../lib/extensions');
  10. /**
  11. * After build
  12. *
  13. * @param {Object} options
  14. * @api private
  15. */
  16. function afterBuild(options) {
  17. var install = sass.getBinaryPath();
  18. var target = path.join(__dirname, '..', 'build',
  19. options.debug ? 'Debug' :
  20. process.config.target_defaults
  21. ? process.config.target_defaults.default_configuration
  22. : 'Release',
  23. 'binding.node');
  24. mkdir(path.dirname(install), function(err) {
  25. if (err && err.code !== 'EEXIST') {
  26. console.error(err.message);
  27. return;
  28. }
  29. fs.stat(target, function(err) {
  30. if (err) {
  31. console.error('Build succeeded but target not found');
  32. return;
  33. }
  34. fs.rename(target, install, function(err) {
  35. if (err) {
  36. console.error(err.message);
  37. return;
  38. }
  39. console.log('Installed to', install);
  40. });
  41. });
  42. });
  43. }
  44. /**
  45. * manageProcess
  46. *
  47. * @param {ChildProcess} proc
  48. * @param {Function} cb
  49. * @api private
  50. */
  51. function manageProcess(proc, cb) {
  52. var errorMsg = '';
  53. proc.stderr.on('data', function(data) {
  54. errorMsg += data.toString();
  55. });
  56. proc.on('close', function(code) {
  57. cb(code === 0 ? null : { message: errorMsg });
  58. });
  59. }
  60. /**
  61. * initSubmodules
  62. *
  63. * @param {Function} cb
  64. * @api private
  65. */
  66. function initSubmodules(cb) {
  67. console.log('Detected a git install');
  68. console.log('Cloning LibSass into src/libsass');
  69. var clone = spawn('git', ['clone', 'https://github.com/sass/libsass.git', './src/libsass']);
  70. manageProcess(clone, function(err) {
  71. if (err) {
  72. cb(err);
  73. return;
  74. }
  75. console.log('Checking out LibSass to', pkg.libsass);
  76. var checkout = spawn('git', ['checkout', pkg.libsass], { cwd: './src/libsass' });
  77. manageProcess(checkout, function(err) {
  78. cb(err);
  79. });
  80. });
  81. }
  82. /**
  83. * installGitDependencies
  84. *
  85. * @param {Function} cb
  86. * @api private
  87. */
  88. function installGitDependencies(options, cb) {
  89. var libsassPath = './src/libsass';
  90. if (process.env.LIBSASS_EXT || options.libsassExt) {
  91. cb();
  92. } else if (fs.access) { // node 0.12+, iojs 1.0.0+
  93. fs.access(libsassPath, fs.R_OK, function(err) {
  94. err && err.code === 'ENOENT' ? initSubmodules(cb) : cb();
  95. });
  96. } else { // node < 0.12
  97. fs.exists(libsassPath, function(exists) {
  98. exists ? cb() : initSubmodules(cb);
  99. });
  100. }
  101. }
  102. /**
  103. * Build
  104. *
  105. * @param {Object} options
  106. * @api private
  107. */
  108. function build(options) {
  109. installGitDependencies(options, function(err) {
  110. if (err) {
  111. console.error(err.message);
  112. process.exit(1);
  113. }
  114. var args = [require.resolve(path.join('node-gyp', 'bin', 'node-gyp.js')), 'rebuild', '--verbose'].concat(
  115. ['libsass_ext', 'libsass_cflags', 'libsass_ldflags', 'libsass_library'].map(function(subject) {
  116. return ['--', subject, '=', process.env[subject.toUpperCase()] || ''].join('');
  117. })).concat(options.args);
  118. console.log('Building:', [process.execPath].concat(args).join(' '));
  119. var proc = spawn(process.execPath, args, {
  120. stdio: [0, 1, 2]
  121. });
  122. proc.on('exit', function(errorCode) {
  123. if (!errorCode) {
  124. afterBuild(options);
  125. return;
  126. }
  127. if (errorCode === 127 ) {
  128. console.error('node-gyp not found!');
  129. } else {
  130. console.error('Build failed with error code:', errorCode);
  131. }
  132. process.exit(1);
  133. });
  134. });
  135. }
  136. /**
  137. * Parse arguments
  138. *
  139. * @param {Array} args
  140. * @api private
  141. */
  142. function parseArgs(args) {
  143. var options = {
  144. arch: process.arch,
  145. platform: process.platform
  146. };
  147. options.args = args.filter(function(arg) {
  148. if (arg === '-f' || arg === '--force') {
  149. options.force = true;
  150. return false;
  151. } else if (arg.substring(0, 13) === '--target_arch') {
  152. options.arch = arg.substring(14);
  153. } else if (arg === '-d' || arg === '--debug') {
  154. options.debug = true;
  155. } else if (arg.substring(0, 13) === '--libsass_ext' && arg.substring(14) !== 'no') {
  156. options.libsassExt = true;
  157. }
  158. return true;
  159. });
  160. return options;
  161. }
  162. /**
  163. * Test for pre-built library
  164. *
  165. * @param {Object} options
  166. * @api private
  167. */
  168. function testBinary(options) {
  169. if (options.force || process.env.SASS_FORCE_BUILD) {
  170. return build(options);
  171. }
  172. if (!sass.hasBinary(sass.getBinaryPath())) {
  173. return build(options);
  174. }
  175. console.log('Binary found at', sass.getBinaryPath());
  176. console.log('Testing binary');
  177. try {
  178. require('../').renderSync({
  179. data: 's { a: ss }'
  180. });
  181. console.log('Binary is fine');
  182. } catch (e) {
  183. console.log('Binary has a problem:', e);
  184. console.log('Building the binary locally');
  185. return build(options);
  186. }
  187. }
  188. /**
  189. * Apply arguments and run
  190. */
  191. testBinary(parseArgs(process.argv.slice(2)));