Saturday, January 21, 2012

Boost.build bjam file to support Google protobuf compilation

This source code was adapted from Rodrigo Pinho Pereira de Souza ( pinhopro at gmail dot com )'s code and is now published under Boost License (as it was).

Usage:

1. Save the following source code into a file pb.jam;
2. Modify boostlib/tools/build/v2/user-config.jam:
and make the following changes;
using pb : /usr/local ;  # /usr/local is the prefix pointing to the Google protobuf installation;
3. In your project jam file

exe myexe : 
   message.proto 
   a.cpp b.cpp

    ; 

or
lib msg1 :   message.proto  ;
 exe myexe : 
   msg1
   a.cpp b.cpp
   : msg1 ;


Finally the source for pb.jam
# Copyright 2012 Liping Zhang (zhanglpg at gmail dot com)
# Copyright 2010 Rodrigo Pinho Pereira de Souza ( pinhopro at gmail dot com )
#
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)

import type ;
import generators ;
import feature ;
import common ;
import "class" : new ;

import modules ;
import feature ;
import project ;
import toolset : flags ;

project.initialize $(__name__) ;
project pb ;


type.register PROTO : proto ;

.project = [ project.current ] ;

# Helper utils for easy debug output
if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ]
{
  .debug-configuration = TRUE ;
}

local rule debug-message ( message * )
{
  if $(.debug-configuration) = TRUE
  {
    ECHO notice: [protobuf-cfg] $(message) ;
  }
}


rule init ( prefix : condition * )
{
  project.push-current $(.project) ;

  .prefix = $(prefix) ;

  .incprefix = $(prefix)/include ;
  .libprefix = $(prefix)/lib ;
  .binprefix = $(prefix)/bin ;   

  debug-message  "rule init (" $(prefix) "," $(condition) ")" ;
  debug-message "include path: " $(.incprefix) ;
  debug-message "library path: " $(.libprefix) ;
  debug-message "bin path" $(.binprefix) ;
  
  if ! $(.initialized)
  {
    .initialized = true ;
    debug-message "initializing protobuf..." ;

    debug-message "registering proto type " ;
    #type.register PROTO : proto ;

    debug-message "registering protoc compiler " ;
    generators.register [ new proto-generator pb.protoc : PROTO : CPP(%.pb) H(%.pb) ] ;

    .PREFIX = $(prefix) ;
    debug-message "protobuf initialized." ;
  }

  debug-message "setup path for protoc tool" ;
  toolset.flags pb.protoc .BINPREFIX : $(.binprefix) ;

  local usage-requirements =
      $(.incprefix)
      $(.libprefix)
      $(.libprefix)
    ;
  debug-message "usage-requirements: " $(usage-requirements) ;

  local target-requirements = $(condition) ;

  lib protobuflib : 
    : # requirements 
      protobuf
       $(target-requirements)
    : # default-build
    : # usage-requirements
      $(usage-requirements)
    ;

  lib protobuf-litelib : 
    : # requirements 
      protobuf-lite
       $(target-requirements)
    : # default-build
    : # usage-requirements
      $(usage-requirements)
    ;

  project.pop-current ;

  debug-message "module pb initialized" ;
}


rule initialized ( )
{
  return $(.initialized) ;
}

class proto-generator : generator 
{
    import "class" : new ;

    rule __init__ ( * : * )
    {
        generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
    }

    rule run ( project name ? : property-set : sources * )
    {
        if ! $(sources[2])
        {
            # Accept only single source.
            local t = [ $(sources[1]).type ] ;
   
            if $(t) = PROTO
            {
                # The type is correct.

                # If no output name is specified, guess it from sources.
                if ! $(name)
                {
                    name = [ generator.determine-output-name $(sources) ] ;
     name = $(name)".pb" ;
                }

       

    local a = [ new action $(sources[1]) : pb.protoc : $(property-set) ] ;
 # since the default CPP suffix is .cpp and protoc generates .cc files, we need to specify the exact file name for CPP
           local target1 = [ new file-target $(name).cc exact : CPP : $(project) : $(a) ] ;

    local target2 = [ new file-target $(name) : H : $(project) : $(a) ] ;
    
    
    DEPENDS all : [ $(target1).actualize ] ;
    DEPENDS all : [ $(target2).actualize ] ;

    return [ virtual-target.register $(target1) ]
     [ virtual-target.register $(target2) ]
     ;
   }
  }
    }
}

#generators.register [ new proto-generator pb.protoc : PROTO : CPP H ] ;

actions protoc
{
  cp $(>[1]) . 
  $(.BINPREFIX[-1])/protoc $(>[1]:B)$(>[1]:S)  --cpp_out=$(<[1]:D) 
  
}