Boost logo

Boost-Build :

From: Vladimir Prus (ghost_at_[hidden])
Date: 2002-04-11 03:06:43


David Abrahams wrote:

> > 1. First, we write a single rule called "make" or "generate"
> >
> > rule make ( targets + : sources * : command-to-run )
> >
> > This rule would just construct dependency graph (of "virtual
>
> targets"), then
>
> > convert it into actual target and build it. This way we'll
> > - have something working -- it's just nice to have
> > - have chance to play with virtual targets and finilize its design
> > - such rule is usefull anyway
>
> Not sure I understand this suggestion. What is "command-to-run"?
> Is this just a rule which constructs a simple dependency graph of depth
> 2?

I imagine two possible alternatives.
1. It is literal string -- a command that should be used to creates sources
from targets.
2. It is name of a rule. That rule is passed names of actual source targets
and target targets, and should assign appropriate actions somehow. That rule
should not call "DEPENDS" or set "LOCATE"/"SEARCH".

> > 2. Second, we make the link between projects/abstract targets and
> > virtual targets. Abstract targets corresponding to project will have
> > 'generate' method, that should return virtual dependency graph.
> > Ultimately, it should use generators to find that graph. At this stage
> > we'll just have a stub rule that, instread of running the right
> > algorithm, will either contain hardcoded logic, or consult some decision
> > cache.
>
> Please elaborate. I get a vague sense of what you're suggesting, but
> it's not solid for me yet.

I'll try to spell the first milestone in more details now, when I understand
better what I mean -- I now have longer list of what we should (and I think
can) put there. Preliminary note: I might sound like I've just wrote done
every feature possible in the list below, and we can't skip most of them. But
while it makes sense to implement some limited functionality to the degree
where boost.build is usable with one compiler, it's even more important to
make sure we understand how to implement everything. The only way is to
actually implement it until it's clear that no problems with arise later.

* Supported functionality. *

***
First is the 'make' rule I was talking about. Let's extend it a bit, and
allow this usage:

project test
: requirements <inlining>full
;

make foo.obj : foo.cpp : gcc-compile ;
make foo.obj : foo.cpp : gcc-compile : <optimization>full ;

This will create two targets, with different properties (and different
locations, accordingly to project-specific properties, explicit properties,
and ALL_LOCATE_TARGET (which will be renamed, of course) ).

The name of target targets , sources targets and property list will be passed
to the gcc-compile rule, to to whatever it like. At this stage, conversion
from property list to compiler options will be hardcoded.

Probably, we can just ignore build request for now.

But there's a problem -- do we allow creating of identially named main
targets in two rules? I think we'd want to allow the usage above at this
stage. I don't know what to do in general -- can it be possible that
different versions of the same main target have so different sets of
sources/requirements that declaring main target twice is reasonable.

***
Second, let's try to allow getting targets from other subprojects. I believe
it's not hard, but is worthwhile to test. For example.

make foo.exe : *subdir/foo.obj : gcc-link ;

("*" is a tentative syntax to refer to other targets, for lack of better)
What problems are there? First, we need a mechnism to select obj of all the
subvariants that are available in subdir -- i.e. a way of telling which
properties are compatible and which are not -- I don't think we have it ready.
Second, I believe we need a mechanism to bypass that selection and to say:
"give me target with such and such properties". It might sound premature to
think about this, but I'd like to have at least some implementation path for
all such non-mainstream features. Otherwise, we can run in problems later.

Since we use some syntax to refer to targets, we can extend it, for example.

make foo.exe : *subdir/foo.obj/<optimization>off
: gcc-link : <optimization>off;

The slash before "<" is only for readability. BTW, do we assume that "<"
never appear in paths?

And one thing more: we can implement exported requirements at this stage.

***
Third, I would like to implement early one very desirable feature:
multi-project builds. Need to check that

- we can refer to targets in other projects
- the targets are built to the right locations
- that exported requirements work across project boundaries

* Implementation *

I've recently checked in the 'targets.jam' which contains the very first
attempts and target handling. In brief, there's 'abstract-target' class which
corresponds to main target in Jamfile. It's 'generate' method should in
future return a dependency graph, given a build request. That dependency
graph is formed of instances of 'virtual-target' class, which has a method
'actualize' returning the name of actual jam targets.

I propose that make rule would just create a special abstract instance.
Instread of calling generators, it would return a dependency graph directly.
Example again

project test
: requirements <inlining>full
;

make foo.obj : foo.cpp : gcc-compile ;
make foo.obj : foo.cpp : gcc-compile : <optimization>full ;

An abstract target "foo.obj" will be created. It will record information
about both rules. So, when generate rule is called, it will return the
graph with two vertices for targets and two vertices for sources.No logic at
all -- isomorphic to what is written in Jamfile.

Here's a problem

make a b c : d : some-action ;

Will three abstract-targets be created? Or just one? In the latter case, with
what name? Or will we ultimately decide not to give the user such a low-level
rule as 'make'. I have a feeling that would be a mistake.

And another problem comes to mind -- related to accessing targets. Suppose a
perverse user has:

lib cool : cool.cpp ;

And wants to make a symlink from the first subvariant build to /tmp/cool.
In old days, this worked --

local l = [ lib cool : cool.cpp ] ;
SymLink $(l[0]) : /tmp ; # might have swapped parameters

Later, I had to wrap this in

if ! $(gIN_LIB_INCLUDE)

In new build system, what will [ lib cool : cool.cpp ] return? It can't
return a list of subvariants targets because we do not know it yet! And some
symbolic name won't work! Will be have to rewrite Jambase completely?

***

Okay, this message was a little bit long... Probably we can split the first
milestone even further.... I'm waiting for comments.

- 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