"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"); 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. 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. > "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. > 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 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. 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. > 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.