On 2011-07-13 23:41, Nick Sabalausky wrote:
"Jacob Carlborg"<d...@me.com>  wrote in message
news:ivke5k$2m78$1...@digitalmars.com...

First I have to say that I know you are doing this because you want to use
D as the language for the build scripts. The reason I did choose Ruby
because I think D will be too verbose and when I'm looking at drakefile.d
I do think it's too verbose.

Yea, D is likely to be a little more verbose than what could be done in Ruby
(or Python). Personally, I think that's well worth it, though. I don't know
how many others would agree or not.


But instead of starting a debate between Ruby and D I'm going to give you
a few suggestions and comments instead.

The minimal drakefile doesn't look that minimal.

I think I may be able to make the "immutable modes = [];" optional by
checking for it with some compile-time reflecion.

The other parts I'll address further below...


This is the idea I have for a minimal build script file:

target("main.d"); // builds "main.d" as an executable

Or:

target("foobar"); // builds the directory "foobar" as a library

DSSS did this very good.


I've never been very comfortable with build systems magically understanding
and inferring all those details about a language and filetypes. It just
feels too "magic", it's much more difficult to expand to new
languages/tools, and every time I use a system like that I feel completely
lost every time I need to include even just one custom buildstep.

However, Drake's design is expandable. Add-on modules can be created that
define new target types, so you could do:

     module drake_dlang;
     class DLang : Target (or File)
     {
         //...etc
     }

     target!DLang("main.d");

And that would essentially wrap a File target, adding in any D-specific
things such as "The target is 'main.exe'". And if you really wanted you
could take it one more step and do:

     void targetD(string source)
     {
         target!DLang(source);
     }

     targetD("main.d");


If that's possible, then great, I was hoping this could be default. I see no reason why it should be more complicated or requires more syntax to such a simple thing as building a standard executable with default configurations. If it's not this easy by default, then I think the build tool have failed, just my opinion.

Good add-ons could even be folded into the standard Drake.


Now looking in the drakefile.d. You have targets and tasks all over the
place, almost every target has "Task" as a template parameter. Why is this
necessary? Have one function for targets and one for tasks. Example from
the drakefile.d:

         target!Task("upload", "all",
                 (Target t)
                 {
                         system("ftp ...");
                 }
         );

Seems this could look like this:

task("upload", "all", {
     system("ftp...");
}); // if D just could get a better looking delegate syntax


Some Drake terminology first: Every "node" in the dependency tree is a
"Target". There are three pre-defined types of targets: "Task", "File" and
"Dir". Other new target types can also be defined. So the template param is
due to that.

However, I suppose it may be a good idea for every target type to come with
a matching shortcut:

     alias target!Task task;
     alias target!File file;
     alias target!Dir  dir;

Then your "task("upload", ...);" example should work.

Ok, now I see. I think this is quite important the you first show the most simple forms and then show what they actually are.

I agree that D's delegate syntax could be better. I think something like
your example would be possible, but I ran into what seemed like some type
inference limitations with all the overloading and templates, etc.

Ok.

"getDCompiler" seems unnecessary, both ldc and gdc have a dmd compatible
wrapper, ldmd and gdmd. These wrappers takes the same flags as dmd and
translate them to the correct "native" flags.


That's great, I didn't know that. But, you still have to say "ldmd" or
"gdmd" instead of "dmd". And since the idea of "getDCompiler" is to allow
the user to select which compiler to use (if the drakefile's author wants to
support that), something roughly like "getDCompiler" may still be needed,
albeit in a greatly simplified form.

In any case, it was just an example of using the "modes" feature.

Ok, I see. I would prefer if the tool had built-in support for this. Just calling a function named "compiler" and it will use that particular compiler.

The "drakfile" function seems unnecessary. I would use a string import (or
what it's called), importing the whole build script into a method in a
class. All code in the "drakefile" function would instead be at top level.


I'm not entirely opposed to that idea, but here are the (perhaps minor?)
reasons I did it this way:

- Using "import" inside functions is brand-new and likely still buggy. And
unless I'm mistaken, I think there's other problems that still exist with
using the same code inside a function as outside (such as some
order-of-declaration limitations, IIRC). I wanted to get moving on something
that would work reliably right away without being too sensitive to bleeding
edge-cases. I figured if all that gets sorted out later on, then maybe a
Drake v2 could go that route.

I was referring to this:

void main ()
{
    writeln(import("main.d"));
}

This has been working as long as I've been using D. It works in D1 as well.

- I wasn't sure if a D file that isn't strictly a proper D file would be too
weird, too confusing, too magical, or would confuse the fuck out of advanced
IDE's.

I have no idea how an IDE would behave with an incomplete file like that. I pretty sure this only matters if the IDE does some semantic processing, i.e. something more than just syntax highlighting. Textmate, which basically only does syntax highlighting, has no problem with an incomplete D file.

If those are really just totally bullshit reasons, then I'm certainly open
to the idea of doing it as you suggest.


Most of the functions called in the build script should be instance
methods, this will allow to use threading, possible invoke multiple
targets/tasks simultaneously and running several build scripts
simultaneously.


My understanding is that with thread-local being the default, they *don't*
need to be instance methods to be thread-safe.

Also, dependency-checking and task-invocation (once they're implemented)
won't occur at all until *after* the drakefile() function completes. The
target!...(...) function merely defines the targets, adding them to an
internal list - nothing more. So I'm not sure that's not really a
thread-appropriate matter.

Not really sure what you mean about running several build scripts
simultaneously. The buildscript gets compiled into an exe, so the
buildscripts all run in separate processes anyway.

Hm, it may actually not be a problem. I'll have to give this some more thought, if there is any problem or not. It's probably me doesn't think straight.

I see installation related code in the build script. I think a package
manager should be responsible for that. A build tool should deal with
single files and a package manager should deal with packages (of files).


Those are just there as examples. You can do it however you want.


-------------------------------
Not sent from an iPhone.



Ok, I see.

--
/Jacob Carlborg

Reply via email to