Boost logo

Boost-Build :

From: Vladimir Prus (ghost_at_[hidden])
Date: 2004-06-18 03:45:04


Hi Emiliano,

> Hello! I've been trying to extend Boost.Build v2 system so it will
> be able to compile java files and generate jars. Ive created a base
> system mainly from builtin.jam called java.jam that added a few type-
> declarations and basic-targets and rules. I also added a register-
> java-compiler.

That's great. You know, it's one of the nicest things in open-source when you
sit buried in some hacking at work, and suddenly a user sends some new
functionality!

> So currently I get X.java compiled into com/yyy/X.class. But the
> system is expecting something <build-dir>\bin\jdk\debug\X.class. Is
> there anyway to override this into "<build-
> dir>\bin\jdk\debug\com/yyy/X.class"? (I'm willing to regex source
> file for package definition).

I think I'd have to first ask how do you plan to use it? There are two
approaches:

1. You drop all .java files in the same directory and they are compiled in
the 'right' directories.
2. You arrange source files according to package name.

I'm not Java programmer, but it seems that a number of projects (e.g. all of
http://jakarta.apache.org) use the second convention.

In this case you need not parse source files for package name, but just need
to make sure V2 does not strip paths from names of sources. However, we
traditionally allowed relative paths in sources, like:

sources = a b c ;
exe a : ../src/$(sources) ;

so maybe this 'don't strip path to source' behaviour should be java-specific.
See below for a possible approach.

Also, another question from a Java niebie: it's it always necessary to compile
all sources in one invocation, or one can compile them one-by-one?

> type.register JAR_LIB : jar : : main ;
> type.register JAVA : java ;
> type.register CLASS : class ;
>
> class jar-target-class : basic-target
> {
> import generators : construct : generators.construct ;
> import type ;
> import path ;
>
> rule __init__ ( name : project
>
> : sources * : requirements * : default-build * : usage-
>
> requirements * )
> {
> basic-target.__init__ $(name) : $(project)
>
> : $(sources) : $(requirements) : $(default-build) :
>
> $(usage-requirements) ;
> }
>
> rule construct ( source-targets * : property-set )
> {
> local properties = [ $(property-set).raw ] ;
> # Determine the needed target type
> local actual-type ;
> actual-type = JAR_LIB ;
> property-set = [ $(property-set).add-raw <main-target-
> type>LIB ] ;
> # Construct the target.
> return [ generators.construct $(self.project) $(self.name) :
> $(actual-type)
>
> : $(property-set) : $(source-targets) :
>
> LIB ] ;
> }
> }

It looks like the only thing this class does is pass JAR_LIB as type to
generators.construct. In that case you probably can drop this class
completely, and ....

> rule jar ( name : sources * : requirements * : default-build *
>
> : usage-requirements * )
>
> {
> local project = [ CALLER_MODULE ] ;
>
> # This is a circular module dependency, so it must be imported
> here
> import targets ;
> targets.main-target-alternative
> [ new jar-target-class $(name) : $(project)

... create typed-target here, passing JAR_LIB as type. BTW, are you working
with m9.1 or with CVS. If with CVS, then you better use [ project.current ]
instead of [ CALLER_MODULE ] . With CALLER_MODULE the 'jar' rule can be only
invoked in Jamfile, and with project.current it can be called from everywhere
-- for example, your project-root.jam can define a helper rule which calls
'jar'.

> class java-compile-action : action
> {
> import sequence ;
>
> rule __init__ ( targets + : sources * : action-name : properties
> * )
> {
> action.__init__ $(targets) : $(sources) : $(action-name) :
> $(properties) ;
> }
>
> # For all virtual targets for the same dependency graph as self,
> # i.e. which belong to the same main target, add their
> directories
> # to include path.
> rule adjust-properties ( properties * )
> {
> local s = [ $(self.targets[1]).creating-subvariant ] ;
> return $(properties) [ $(s).implicit-includes "include" ] ;
> }
> }

I'm not sure this rule (adjust-properties) is necessary. For C, it makes sure
that every .h header which is created in the same main target is in include
path. For example:

exe a : a.cpp parser.y ;

here parser.h is created from parser.y and is put in bin/debug... but will
should be found by #include "parser.h" in a.cpp. In Java, we have import, and
I guess that this functionality is not needed.

I think that you can handle the names issue by redefining the
'generated-targets' rule in this generators. For each Java source, you can
get its path with:

local p = [ $(s).project ] ;
local location = [ path.root [ $(s).name ]
[ $(p).get source-location ] ] ;

after that you can compute relative path from source location with
path.relative. After that, you can adjust the name of generated targets.
The result would be that com/something/X.java can be compiled to
bin/debug/com/something/X.class.

I did not try to implement this myself, so there might be problems along there
way. In that case, don't hesitate to ask.

> # Declare generators
> generators.register-composing jdk.archive : CLASS : JAR_LIB :
> <toolset>jdk ;
> generators.register-java-compiler jdk.compile : JAVA : CLASS :
> <toolset>jdk ;
>
> # Declare flags and action for compilation
> actions compile
> {
> $(NAME:E=javac) $(OPTIONS) "$(>)"
> }
>
> # Declare action for creating static libraries
> actions piecemeal archive
> {
> jar -cf "$(<)" "$(>)"
> }

I guess you'll add support for the "classpath" feature later?

- Volodya

 


Boost-Build list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk