[cookbook] GYP Quick Review (Generate Your Projects)

GYP (Generate Your Projects) Quick Review

hello-world gyp addon 미리보기

ryahgyp hello-world addontest/addons/hello-world 안에 제공하였습니다. 이것을 기준으로 설명을 하고, 간단한 응용예제도 포함하겠습니다.

파일구성

test.js

var assert = require('assert');
//
// gyp_addon && make 에 의해 빌드된 binding.node 가져오기
//
var binding = require('./build/Release/binding');
assert.equal('world', binding.hello());
console.log('binding.hello() =', binding.hello());

binding.gyp

{
  'targets': [
    {
      #
      # 생성할 모듈이름 (modulename.node)
      #
      'target_name': 'binding',
      #
      # 소스파일
      #
      'sources': [ 'binding.cc' ]
    }
  ]
}

binding.cc

#include <node.h>
#include <v8.h>

using namespace v8;

Handle<Value> Method(const Arguments& args) {
  HandleScope scope;
  return scope.Close(String::New("world"));
}

void init(Handle<Object> target) {
  NODE_SET_METHOD(target, "hello", Method);
}

NODE_MODULE(binding, init);

gyp_addon 실행

$ cd node-v0.8.1/test/addons/hello-world
$ ../../../tools/gyp_addon

결과

$ ls
binding.cc  binding.gyp  build  test.js

build 디렉토리 생성 확인

$ cd build
$ ls
Makefile  Release  binding.Makefile  binding.target.mk

Makefile 생성확인

$ vi binding.target.mk +25

25 INCS_Debug := -I/home/dev/node-v0.8.1/src \\
26   -I/home/dev/node-v0.8.1/deps/uv/include \\
27   -I/home/dev/node-v0.8.1/deps/v8/include \\

기본적으로 node의 src, deps 디렉토리가 include 되어 있습니다. 그리하여 binding.cc에서 node.h, v8.h 를 include 할 수 있습니다.

make 실행

$ make
  CXX(target) Release/obj.target/binding/binding.o
  SOLINK_MODULE(target) Release/obj.target/binding.node
  SOLINK_MODULE(target) Release/obj.target/binding.node: Finished
  COPY Release/binding.node

Release target 디렉토리 확인

$ ls Release/
binding.node  linker.lock  obj.target

binding.node 파일 확인할 수 있습니다.

javascript 실행

$ node test.js
binding.hello() = world

gethostname 가져오기 예제

hostname 가져오는 심플한 다른 예제를 첨부로 작성해보았습니다.

test.js

var binding = require('./build/Release/binding');

//
// getHostname
//
console.log(binding.getHostname());

binding.gyp

{
  'variables': {
      #
      # targets에서 사용할 수 있는 변수 선언
      # targets에서 <@(noderoot) 식으로 사용가능
      #
      'noderoot': '../../../'
  },
  'targets': [
    {
      #
      # 생성할 모듈이름 (modulename.node)
      #
      'target_name': 'binding',
      'sources': [ 'binding.cc' ],
      'conditions': [
        #
        # OS linux condition 추가
        #
        [ 'OS=="linux"', {
          #
          # unistd.h 들어있는 include_dir 추가
          # gethostname 함수 포함 파일
          #
          'include_dirs': [
            '/usr/include',
          ]
        } ]
      ]
    }
  ]
}

>> GYP Manual 참조

binding.cc

#include <node.h>
#include <v8.h>

//
// gethostname 함수 선언되어 있는 헤더
//
#include <unistd.h>

using namespace v8;

//
// hostname 가져오기
//
static Handle<Value> GetHostname(const Arguments& args) {
  HandleScope scope;
  char s[255];
  gethostname(s, 255);

  return scope.Close(String::New(s));
}

void init(Handle<Object> target) {
  //
  // property 설정
  //
  NODE_SET_METHOD(target, "getHostname", GetHostname);
}

NODE_MODULE(binding, init);

결과

위 예제와 다른 부분만 설명하겠습니다.

$ vi binding.target.mk +25

25 INCS_Debug := -I/home/dev/node-v0.8.1/src \\
26   -I/home/dev/node-v0.8.1/deps/uv/include \\
27   -I/home/dev/node-v0.8.1/deps/v8/include \\
28   -I/usr/include

gyp condition에서 작성한 /usr/include 포함됨

javascript 실행

$ node test.js
nodejs.cafe24.com

TroubleShooting

gethostname declare 관련

lstdc++ 관련 에러

결론

ryah가 GYP를 너무나도 사랑스러워해서 (요즘 <3 이거 연발) GYP에 대한 퀵리뷰를 작성했습니다. 제가 느낀점은 확실히 WAF 보다는 간편하고 직관적이라는 것입니다. 그럼 이만. ~ 감사합니다.

추천 관련글