Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add npm dependencies to engine descriptions #65

Closed
wants to merge 1 commit into from

Conversation

tastycode
Copy link

After installing grunt-spritesmith
npm install grunt-spritesmith --save-dev

and setting it up to load some icons originally from a rails project

   //grunt config
    sprite:{
      all: {
        src: ['<%= yeoman.app %>/../../app/assets/images/icons/*.png'],
        destImg: '<%= yeoman.app %>/images/icons.png',
        destCSS: '<%= yeoman.app %>/styles/icons.scss',
        cssFormat: 'scss'
      }
    },

after running grunt sprite --stack I get this

Running "sprite:all" (sprite) task
Fatal error: spawn ENOENT
Error: spawn ENOENT
    at errnoException (child_process.js:980:11)
    at Process.ChildProcess._handle.onexit (child_process.js:771:34)

I found where we are calling spawn

$ cd node_modules/grunt-spritesmith/
$ grep -R spawn .
./node_modules/spritesmith/node_modules/gmsmith/node_modules/gm/lib/command.js:var spawn = require('child_process').spawn;
./node_modules/spritesmith/node_modules/gmsmith/node_modules/gm/lib/command.js:      self._spawn(self.args(), true, callback);
./node_modules/spritesmith/node_modules/gmsmith/node_modules/gm/lib/command.js:      return self._spawn(self.args(), false, callback);
./node_modules/spritesmith/node_modules/gmsmith/node_modules/gm/lib/command.js:    return this._spawn(args, true, callback);
./node_modules/spritesmith/node_modules/gmsmith/node_modules/gm/lib/command.js:  proto._spawn = function _spawn (args, bufferOutput, callback) {
./node_modules/spritesmith/node_modules/gmsmith/node_modules/gm/lib/command.js:    var proc = spawn(bin, args)
./node_modules/spritesmith/node_modules/pngsmith/node_modules/concat-stream/test.js:var spawn = require('child_process').spawn
./node_modules/spritesmith/node_modules/pngsmith/node_modules/concat-stream/test.js:var cmd = spawn('ls')

The spawn is trying to launch "convert" or "identify", but those binaries exist. Assuming there is some irreparable bug, I moved on to a few other less mature sprite frameworks with less success.

I gave grunt-spritesmith another shot, this time searching for the node.js call for spawn, which apparently is compiled. I fired up dtrace to see the underlying syscalls. Here is the output when getting the spawn ENOENT

$ sudo dtruss -n node
//...
58995/0x1da156d:  ioctl(0x2, 0x8004667E, 0x7FFF5FBFE95C)     = 0 0
58995/0x1da156d:  execve("/Users/hulahoop/.rvm/gems/ruby-2.0.0-p353@rally/bin/gm\0", 0x100C00FF0, 0x100C01CD0)     = -1 Err#2
58995/0x1da156d:  execve("/Users/hulahoop/.rvm/gems/ruby-2.0.0-p353@global/bin/gm\0", 0x100C00FF0, 0x100C01CD0)    = -1 Err#2
58995/0x1da156d:  execve("/Users/hulahoop/.rvm/rubies/ruby-2.0.0-p353/bin/gm\0", 0x100C00FF0, 0x100C01CD0)     = -1 Err#2
58995/0x1da156d:  execve("/Users/hulahoop/.rvm/bin/gm\0", 0x100C00FF0, 0x100C01CD0)    = -1 Err#2
58995/0x1da156d:  execve("/usr/local/sbin/gm\0", 0x100C00FF0, 0x100C01CD0)     = -1 Err#2
58995/0x1da156d:  execve("/usr/local/bin/gm\0", 0x100C00FF0, 0x100C01CD0)    = -1 Err#2
58995/0x1da156d:  execve("/usr/bin/gm\0", 0x100C00FF0, 0x100C01CD0)    = -1 Err#2
58995/0x1da156d:  execve("/bin/gm\0", 0x100C00FF0, 0x100C01CD0)    = -1 Err#2
58995/0x1da156d:  execve("/usr/sbin/gm\0", 0x100C00FF0, 0x100C01CD0)     = -1 Err#2
58995/0x1da156d:  execve("/sbin/gm\0", 0x100C00FF0, 0x100C01CD0)     = -1 Err#2
58995/0x1da156d:  execve("/opt/X11/bin/gm\0", 0x100C00FF0, 0x100C01CD0)    = -1 Err#2
58995/0x1da156d:  execve("/usr/X11/bin/gm\0", 0x100C00FF0, 0x100C01CD0)    = -1 Err#2
58995/0x1da156d:  write(0x12, "\002\0", 0x4)     = 4 0

Apparently there is some bug loading the "gm" spritesmith engine. I don't have graphicsmagic installed on the system anywhere, and couldn't find it with brew.

After reading the spritesmith docs it seemed that phantomjs would be the better choice. The docs say I just need to make sure phantomjs is installed.

brew install phantomjs
grunt sprite --stack
Error loading phantomjs { [Error: Cannot find module 'phantomjssmith'] code: 'MODULE_NOT_FOUND' }
Running "sprite:all" (sprite) task
Warning: Sorry, the spritesmith engine 'phantomjs' could not be loaded. Please be sure you have installed it properly on your machine. Use --force to continue.

Digging through the source, I found some troubling code.

var canvasEngine,
    gmEngine,
    phantomjsEngine,
    pngEngine;
try {
  canvasEngine = require('canvassmith');
} catch (e) {}

try {
  gmEngine = require('gmsmith');
} catch (e) {}

try {
  phantomjsEngine = require('phantomjssmith');
} catch (e) {}

try {
  pngEngine = require('pngsmith');
} catch (e) {}

if (canvasEngine) { addEngine('canvas', canvasEngine); }
if (gmEngine) { addEngine('gm', gmEngine); }
if (phantomjsEngine) { addEngine('phantomjs', phantomjsEngine); }
if (pngEngine) { addEngine('pngsmith', pngEngine); }

I did npm install phantomjssmith --save-dev then ran grunt sprite and everything worked!

It seems that the main README.md does not mention these packages have to be installed to use the engines.

I'm going to err on the side that there is something obvious that I missed so that I had to go down this rabbit hole. In case I didn't miss anything, here is a PR to clear this up a bit.

@twolfson
Copy link
Owner

If you don't specify an engine, grunt-spritesmith will run down a list of automatically available engines. If none are found, it will present an error which is listed in the FAQs.

https://github.com/Ensighten/grunt-spritesmith#spritesmith-is-saying-no-spritesmith-engine-could-be-loaded-for-your-machine

The initial error you received sounds like you copy/pasted the entirety of the config including engineOpts for using image-magick.

The reason this error was appearing is dependencies are checked when grunt-spritesmith is installed initially. Installing the requirements afterwards causes this mismatch of logic. If you re-install grunt-spritesmith, everything should work as well.

npm install grunt-spritesmith  # npm will use the version already listed in `package.json`

If this error was happening without you specifying engine in your configuration, please let me know as that is a regression.

@twolfson twolfson closed this Jan 31, 2014
@tastycode
Copy link
Author

The initial error you received sounds like you copy/pasted the entirety of the config including engineOpts for using image-magick.

Here is the entirety of the initial grunt-spritesmith config.

   //grunt config
    sprite:{
      all: {
        src: ['<%= yeoman.app %>/../../app/assets/images/icons/*.png'],
        destImg: '<%= yeoman.app %>/images/icons.png',
        destCSS: '<%= yeoman.app %>/styles/icons.scss',
        cssFormat: 'scss'
      }
    },

If this error was happening without you specifying engine in your configuration, please let me know as that is a regression.

That is the case, no engine is specified above.

@twolfson
Copy link
Owner

twolfson commented Feb 5, 2014

Well that is no fun. Can you tell me what version of grunt-spritesmith, grunt, and node you are running?

npm ls grunt-spritesmith
npm ls grunt
node --version

@tastycode
Copy link
Author

$ npm ls grunt-spritesmith
npm ls grunt
node --versionrally@0.0.0 /Users/hulahoop/workspace/rally/ng
└── grunt-spritesmith@1.21.2

$ npm ls grunt
node --versionrally@0.0.0 /Users/hulahoop/workspace/rally/ng
└── grunt@0.4.2

$ node --version
v0.10.22

@twolfson
Copy link
Owner

twolfson commented Feb 6, 2014

Cool, thanks. I will take a look by the end of the weekend.

@twolfson
Copy link
Owner

twolfson commented Feb 6, 2014

Alright, I matched up your environment package for package and could not reproduce.

https://gist.github.com/twolfson/8838601

The 'could not be loaded' text only appears when you specify an engine. I am leaving this issue as closed.

https://github.com/Ensighten/spritesmith/blob/0.17.4/src/smith.js#L33

@tastycode
Copy link
Author

:+1 thanks for looking into...

There is definitely a problem. [It appears to be assert.strictEqual doesn't do what it seems intended to do.](https://www.dropbox.com/s/la5kx2r7enktr8a/spritesmith-enoent.mov)
> var assert = require('assert');
undefined
> assert.equal('auto', 'auto', 'this does nothing')
undefined
> assert.notEqual('auto', 'auto', 'this does what you intend, but reading the code does not make sense')
AssertionError: this does nothing
    at repl:1:9
    at REPLServer.self.eval (repl.js:110:21)
    at Interface.<anonymous> (repl.js:239:12)
    at Interface.EventEmitter.emit (events.js:95:17)
    at Interface._onLine (readline.js:202:10)
    at Interface._line (readline.js:531:8)
    at Interface._ttyWrite (readline.js:760:14)
    at ReadStream.onkeypress (readline.js:99:10)
    at ReadStream.EventEmitter.emit (events.js:98:17)
    at emitKey (readline.js:1095:12)
> assert.notEqual('meow', 'auto', 'this does nothing')
undefined

In the line you referenced

https://github.com/Ensighten/spritesmith/blob/0.17.4/src/smith.js#L33

assert.strictEqual(enginePref, 'auto', 'Sorry, the spritesmith engine \'' + enginePref + '\' could not be loaded. Please be sure you have installed it properly on your machine.');
It seems your intent is "if the enginePref is equal to auto, then stop everything and show this message".

When replaced with

assert.notEqual(enginePref, 'auto', 'Sorry, the spritesmith engine \'' + enginePref + '\' could not be loaded. Please be sure you have installed it properly on your machine.');

I get the intended effect of the message being displayed on the screen. However, the code doesn't read well that way.

if (enginePref === 'auto') {
   throw new Error('RAWR');
}
Seems to accomplish your intent and is a bit more readable.

Edit: See next comment

@tastycode
Copy link
Author

I'm an idiot. Your code for checking that assertion is fine. I still get ENOENT. Even with out specifying the engine. It turns out gmsmith is getting installed, the engine loading code detects it and then tries to use it, however GM doesn't exist on my system (hence the dtrace stuff indicating gm was the problem`.

It looks like spritesmith is including gmsmith in its actual dependencies.

$ grep -A4 dependencies grunt-spritesmith/node_modules/spritesmith/package.json
  "dependencies": {
    "async": "~0.2.6",
    "layout": "~1.3.0",
    "phantomjssmith": "~0.4.0",
    "gmsmith": "~0.3.0",
--
//...

This totally differs from your package.json on spritesmith. https://github.com/Ensighten/spritesmith/blob/0.17.4/package.json#L19

I reinstalled grunt-spritesmith and reinstalled and verified it is installing gmsmith.

$ npm install grunt-spritesmith
npm WARN package.json rally@0.0.0 No description
npm WARN package.json rally@0.0.0 No repository field.
npm WARN package.json rally@0.0.0 No README data
npm http GET https://registry.npmjs.org/grunt-spritesmith
npm http 304 https://registry.npmjs.org/grunt-spritesmith
npm http GET https://registry.npmjs.org/spritesmith
npm http GET https://registry.npmjs.org/json2css
npm http GET https://registry.npmjs.org/underscore
npm http GET https://registry.npmjs.org/url2
npm http 304 https://registry.npmjs.org/underscore
npm http 304 https://registry.npmjs.org/url2
npm http 304 https://registry.npmjs.org/json2css
npm http 304 https://registry.npmjs.org/spritesmith
npm http GET https://registry.npmjs.org/mustache
npm http GET https://registry.npmjs.org/json-content-demux
npm http GET https://registry.npmjs.org/async
npm http GET https://registry.npmjs.org/layout
npm http GET https://registry.npmjs.org/gmsmith
npm http GET https://registry.npmjs.org/canvassmith
npm http GET https://registry.npmjs.org/pngsmith
npm http 304 https://registry.npmjs.org/async
npm http 304 https://registry.npmjs.org/json-content-demux
npm http 304 https://registry.npmjs.org/layout
npm http 304 https://registry.npmjs.org/canvassmith
npm http 304 https://registry.npmjs.org/pngsmith
npm http 304 https://registry.npmjs.org/gmsmith
npm http 304 https://registry.npmjs.org/mustache
npm http GET https://registry.npmjs.org/obj-extend
npm http GET https://registry.npmjs.org/temporary/0.0.5
npm http GET https://registry.npmjs.org/canvas
npm http GET https://registry.npmjs.org/binpacking/0.0.1

> gmsmith@0.3.0 preinstall /Users/hulahoop/workspace/rally/ng/node_modules/grunt-spritesmith/node_modules/spritesmith/node_modules/gmsmith
> gm -version || convert -version

sh: gm: command not found
Version: ImageMagick 6.8.7-0 2013-11-22 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2013 ImageMagick Studio LLC
Features: DPC
npm http GET https://registry.npmjs.org/ndarray
npm http GET https://registry.npmjs.org/get-pixels
npm http GET https://registry.npmjs.org/save-pixels
npm http GET https://registry.npmjs.org/concat-stream
npm http GET https://registry.npmjs.org/gm
npm http 304 https://registry.npmjs.org/obj-extend
npm http 304 https://registry.npmjs.org/canvas
npm http 304 https://registry.npmjs.org/ndarray

Checking on the npm registry itself shows this

curl -v https://registry.npmjs.org/spritesmith | python -m json.tool
* About to connect() to registry.npmjs.org port 443 (#0)
*   Trying 199.27.79.162...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* connected
* Connected to registry.npmjs.org (199.27.79.162) port 443 (#0)
* SSLv3, TLS handshake, Client hello (1):
} [data not shown]
* SSLv3, TLS handshake, Server hello (2):
{ [data not shown]
* SSLv3, TLS handshake, CERT (11):
{ [data not shown]
* SSLv3, TLS handshake, Server finished (14):
{ [data not shown]
* SSLv3, TLS handshake, Client key exchange (16):
} [data not shown]
* SSLv3, TLS change cipher, Client hello (1):
} [data not shown]
* SSLv3, TLS handshake, Finished (20):
} [data not shown]
* SSLv3, TLS change cipher, Client hello (1):
{ [data not shown]
* SSLv3, TLS handshake, Finished (20):
{ [data not shown]
* SSL connection using RC4-SHA
* Server certificate:
*    subject: C=US; ST=CA; L=San Francisco; O=Joyent, Inc.; CN=nodejs.org
*    start date: 2012-08-24 19:33:10 GMT
*    expire date: 2014-08-25 19:33:10 GMT
*    subjectAltName: registry.npmjs.org matched
*    issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign Organization Validation CA - G2
*    SSL certificate verify ok.
> GET /spritesmith HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8y zlib/1.2.5
> Host: registry.npmjs.org
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 06 Feb 2014 22:55:31 GMT
< Server: CouchDB/1.5.0 (Erlang OTP/R16B)
< Etag: "LMWV9IEXOY320QCR0JJ096CP"
< Content-Type: application/json
< Via: 1.1 varnish
< Cache-Control: max-age=1
< Content-Length: 44143
< Accept-Ranges: bytes
< Via: 1.1 varnish
< Age: 11
< X-Served-By: cache-v44-ASH, cache-lax1432-LAX
< X-Cache: MISS, MISS
< X-Cache-Hits: 0, 0
< X-Timer: S1391727320.335172415,VS0,VS34,VE176,VE11229
< Vary: Accept
<
{ [data not shown]
100 44143  100 44143    0     0   135k      0 --:--:-- --:--:-- --:--:--  142k
* Connection #0 to host registry.npmjs.org left intact
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):
} [data not shown]
{
# truncated
     "0.17.4": {
            "_from": ".",
            "_id": "spritesmith@0.17.4",
            "_npmUser": {
                "email": "todd@twolfson.com",
                "name": "twolfson"
            },
            "_npmVersion": "1.3.14",
            "author": {
                "email": "todd@twolfson.com",
                "name": "Todd Wolfson"
            },
            "bugs": {
                "url": "https://github.com/Ensighten/spritesmith/issues"
            },
            "contributors": "Alex Bain <alex@alexba.in",
            "dependencies": {
                "async": "~0.2.6",
                "canvassmith": "~0.2.0",
                "gmsmith": "~0.3.0",
                "layout": "~1.3.0",
                "phantomjssmith": "~0.4.0",
                "pngsmith": "~0.1.0"
            },
            "description": "Utility that takes images and creates a spritesheet with JSON sprite data",
            "devDependencies": {
                "doubleshot": "~1.0.1",
                "underscore": "~1.4.4"
            },
            "directories": {},
            "dist": {
                "shasum": "4ed452c86b3580c9680babaa1fa895507569fd51",
                "tarball": "http://registry.npmjs.org/spritesmith/-/spritesmith-0.17.4.tgz"
            },
            "homepage": "https://github.com/Ensighten/spritesmith",
            "keywords": [
                "sprite",
                "spritesheet",
                "css"
            ],
            "license": "MIT",
            "main": "src/smith.js",
            "maintainers": [
                {
                    "email": "todd@twolfson.com",
                    "name": "twolfson"
                }
            ],
            "name": "spritesmith",
            "optionalDependencies": {
                "canvassmith": "~0.2.0",
                "gmsmith": "~0.3.0",
                "phantomjssmith": "~0.4.0",
                "pngsmith": "~0.1.0"
            },
            "repository": {
                "type": "git",
                "url": "https://github.com/Ensighten/spritesmith.git"
            },
            "scripts": {
                "test": "cd src-test && doubleshot --timeout 60000 --outline smith.outline.js --content smith.content.js"
            },
            "version": "0.17.4"
        },

It looks like all of this has nothing to do with you. I'm sorry for pestering you, it turns out something is crazy with npm.

@tastycode
Copy link
Author

Discussion moved to npm/npm-registry-couchapp#146

@twolfson
Copy link
Owner

twolfson commented Feb 7, 2014

npm has many ways of representing dependencies.

https://npmjs.org/doc/json.html#dependencies

For spritesmith, we use optionalDependencies to attempt to install packages that have dependencies which extend beyond node (e.g. graphicsmagick, imagemagick, cairo). These are documented in the README:

https://github.com/Ensighten/grunt-spritesmith#requirements

Additionally, it leads to the error messages you are seeing during installation. These are non-fatal error messages and are noted in FAQs.

https://github.com/Ensighten/grunt-spritesmith#i-am-seeing-errors-during-installation

It seems that there is an assumptive flaw in gmsmith, the graphicsmagick engine. It installs by properly detecting that either graphicsmagick or imagemagick is installed. However, when running it tries to use graphicsmagick unless imagemagick is stated via engineOpts. This should be more implicit via a which call at load time. I will open an issue on gmsmith.

@twolfson
Copy link
Owner

twolfson commented Feb 8, 2014

The regression has been patched in gmsmith@0.4.0 and grunt-spritesmith@1.22.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants