Integrate rdmd into compiler?

Dave Dave_member at pathlink.com
Tue Apr 25 07:38:54 PDT 2006


Daniel Keep wrote:
> AFAIK it has been integrated; not sure when it happened.  Try `dmd -run
> SCRIPT`.  As for gdc, I have no idea.
> 
> 	-- Daniel
>


Thanks - I realize that but didn't make clear what I was asking.. rdmd 
adds a couple features that 'dmd -run' doesn't have:

- caching the executable
- passing in compiler options

I want to know the opinion of others of adding this to dmd itself and 
also opinions on the source code below (e.g.: security holes that I may 
be missing).

Thanks,

- Dave


> Dave wrote:
>> The DMD distro. comes with 'rdmd' which extends the idea of running
>> 'she-bang' sh scripts by caching the executable and also allowing
>> compiler flags in the interpreter command-line (like #!/usr/bin/rdmd -O
>> -version=foo).
>>
>> I find these two things pretty useful...
>>
>> Assuming this would be pretty easy to integrate into the compiler itself
>> and Walter wants to, how about it? That way we could just use g/dmd for
>> 'scripts' too.
>>
>> Opinions?
>>
>> Thanks,
>>
>> - Dave
>>
>> P.S.: Attached is the source code - the things I think need critical
>> judgement would be a big holes, non-POSIX like non-standard cache path,
>> and not recompiling an exe when it should be (source or switch change).
>>
>>
>> ------------------------------------------------------------------------
>>
>> // rdmd - a program to compile, cache and execute D programming
>> // language source files via either the command-line or as a
>> // 'pseudo shell script' on POSIX conforming Linux systems.
>> //
>> // Written by Dave Fladebo and released into the public domain as
>> // explained by http://creativecommons.org/licenses/publicdomain
>> //
>> // This software is provided "AS IS" and without any express or
>> // implied warranties, including and without limitation to, any
>> // warranty of merchantability or fitness for any purpose.
>> //
>> // Windows additions by Roberto Mariottini
>> //
>> // version 1.1
>>
>> version (Windows)
>> {
>>   import std.c.windows.windows;
>>
>>   char[] fileSeparator = "\\";
>>   char[] pathSeparator = ";";
>>   char[] objectExtension = ".obj";
>>   char[] exeExtension = ".exe";
>> }
>> else
>> {
>>   import std.c.linux.linux;
>>
>>   char[] fileSeparator = "/";
>>   char[] pathSeparator = ":";
>>   char[] objectExtension = ".o";
>>   char[] exeExtension = "";
>> }
>>
>> bool verbose = false;
>>
>> import std.c.stdlib, std.file, std.md5, std.process, std.stdio, std.string;
>>
>> int main(char[][] args)
>> {
>>     int retval = -1;
>>     bool havefile = false, force = false;
>>     char[][] cmpv, argv;    // cmpv = compiler arguments, argv = program arguments
>>     char[] exepath, dfilepath, compiler = "dmd", tmpdir = "/tmp";
>>
>>     version (Windows)
>>     {
>>         tmpdir = toString(getenv("TEMP"));
>>     }
>>
>>     .myname = args[0];
>>
>>     foreach(int i, char[] arg; args)
>>     {
>>         if(i == 0)
>>             continue;
>>
>>         if(find(arg,".d") >= 0  ||  find(arg,".ds") >= 0)
>>         {
>>             havefile = true;
>>             dfilepath = arg;
>>         }
>>         else
>>         {
>>             if(havefile == false)
>>             {
>>                 bool skip = false;
>>                 if(arg == "--help")
>>                     usage;
>>                 else if(arg == "--force")
>>                     skip = force = true;
>>                 else if(arg == "--verbose")
>>                     skip = verbose = true;
>>                 else
>>                 {
>>                     const char[] cs = "--compiler=";
>>                     if(arg.length > cs.length && arg[0..cs.length] == cs)
>>                     {
>>                         compiler = split(arg,"=")[1];
>>                         skip = true;
>>                     }
>>                     const char[] td = "--tmpdir=";
>>                     if(arg.length > td.length && arg[0..td.length] == td)
>>                     {
>>                         tmpdir = split(arg,"=")[1];
>>                         skip = true;
>>                     }
>>                 }
>>
>>                 if(!skip)
>>                     cmpv ~= arg;
>>             }
>>             else
>>                 argv ~= arg;
>>         }
>>     }
>>
>>     if(!havefile)
>>         error("Couldn't find any D source code file to compile or execute.", retval);
>>
>>     if(compile(tmpdir,compiler,force,dfilepath,cmpv,exepath))
>>     {
>>         char[][] exeargv;
>>         exeargv ~= exepath;
>>         foreach(char[] arg; argv) exeargv ~= arg;
>>
>>         if(verbose)
>>         {
>>             fwritef(stderr,"running: ");
>>             foreach(char[] arg; exeargv)
>>             {
>>                 fwritef(stderr,arg," ");
>>             }
>>             fwritefln(stderr);
>>         }
>>
>>         // execute
>>         version (Windows)
>>         {
>>             retval = spawnvp(std.c.process._P_WAIT, exepath, exeargv);
>>         }
>>         else
>>         {
>>             retval = spawnapp(exepath,exeargv);
>>         }
>>     }
>>     else
>>     {
>>         try { std.file.remove(exepath); } catch {}
>>         error("Couldn't compile or execute " ~ dfilepath ~ ".", retval);
>>     }
>>
>>     return retval;
>> }
>>
>> char[] myname;
>> void error(char[] errmsg, int errno)
>> {
>>     fwritefln(stderr,myname,": ",errmsg);
>>     exit(errno);
>> }
>>
>> void usage()
>> {
>>     fwritefln(stderr,"Usage:");
>>     fwritefln(stderr,"  ",myname," [D compiler arguments] [",myname," arguments] progfile.d [program arguments]");
>>     fwritefln(stderr);
>>     fwritefln(stderr,myname," arguments:");
>>     fwritefln(stderr,"  --help\t\tThis message");
>>     fwritefln(stderr,"  --force\t\tForce re-compilation of source code [default = do not force]");
>>     fwritefln(stderr,"  --verbose\t\tShow detailed info of operations [default = do not show]");
>>     fwritefln(stderr,"  --compiler=(dmd|gdmd)\tSpecify compiler [default = dmd]");
>>     fwritefln(stderr,"  --tmpdir=tmp_dir_path\tSpecify directory to store cached program and other temporaries [default = /tmp]");
>>     fwritefln(stderr);
>>     fwritefln(stderr,"Notes:");
>>     fwritefln(stderr,"  dmd or gdmd must be in the current user context $PATH");
>>     fwritefln(stderr,"  ",myname," does not support execution of D source code via stdin");
>>     fwritefln(stderr,"  ",myname," will only compile and execute files with a '.d' file extension");
>>     exit(EXIT_SUCCESS);
>> }
>>
>> bool compile(char[] tmpdir, char[] compiler, bool force, char[] dfilepath, char[][] cmpv, inout char[] exepath)
>> {
>>     int retval = 0;
>>
>>     struct_stat dfilestat;  // D source code file status info.
>>     int filrv = stat(dfilepath,&dfilestat);
>>
>>     char[][] pathcomps = split(dfilepath,fileSeparator);
>>     char[] exefile = split(pathcomps[$-1],".")[0];
>>
>>     char[] cmdline = compiler ~ " -quiet";
>>     foreach(char[] str; cmpv)
>>         if(str != "")
>>             cmdline ~= " " ~ str;
>>
>>     // MD5 sum of compiler arguments
>>     ubyte[16] digest;
>>     sum(digest,cast(void[])cmdline);
>>
>>     // directory for temp. files
>>     if(!tmpdir.length)
>>         tmpdir = "/tmp/";
>>     else
>>         if(tmpdir[$-1] != fileSeparator[0])
>>             tmpdir ~= fileSeparator;
>>
>>     // exe filename format is basename-uid-filesysdev-inode-MD5
>>     // append MD5 sum of the compiler arguments onto the file name to force recompile if they have changed
>>     exepath = tmpdir ~ exefile ~ "-" ~ toString(getuid()) ~ "-" ~ toString(dfilestat.st_dev) ~ "-" ~ toString(dfilestat.st_ino) ~ "-" ~ digestToString(digest) ~ exeExtension;
>>
>>     struct_stat exestat;    // temp. executable status info.
>>     int exerv = stat(exepath,&exestat);
>>     if(force ||                                 // force compilation
>>        exerv ||                                 // stat returned an error (e.g.: no exefile)
>>        dfilestat.st_mtime > exestat.st_mtime || // source code file is newer than executable
>>        progstat(.myname).st_mtime > exestat.st_mtime || // this program is newer than executable
>>        progstat(compiler).st_mtime > exestat.st_mtime)  // compiler is newer than executable
>>     {
>>         cmdline ~= " " ~ dfilepath ~ " -of" ~ exepath ~ " -od" ~ tmpdir;
>>         if(verbose)
>>         {
>>             fwritefln(stderr,"running: ",cmdline);
>>         }
>>         retval = system(cmdline);   // compile
>>         chmod(exepath,0700);
>>     }
>>
>>     // remove object file
>>     try { std.file.remove(tmpdir ~ exefile ~ objectExtension); } catch {}
>>
>>     return cast(bool)(retval == 0);
>> }
>>
>> struct_stat progstat(char[] program)
>> {
>>     struct_stat progstat;  // D source code file status info.
>>
>>     try
>>     {
>>         int prgrv;
>>         if(find(program,fileSeparator) >= 0)
>>             prgrv = stat(program, &progstat);
>>         else
>>         {
>>             // There's got to be a better way...
>>             char[][] pathdirs = split(toString(getenv("PATH")),pathSeparator);
>>             foreach(char[] dir; pathdirs)
>>             {
>>                 prgrv = stat(dir ~ fileSeparator ~ program, &progstat);
>>                 if (prgrv == 0)
>>                 {
>>                     break;
>>                 }
>>             }
>>         }
>>     }
>>     catch {}
>>
>>     return progstat;
>> }
>>
>> version(linux)
>> {
>> int spawnapp(char[] pathname, char[][] argv)
>> {
>>     int retval = 0;
>>     pid_t pid = fork();
>>
>>     if(pid != -1)
>>     {
>>         if(pid == 0)
>>         {
>>             execv(pathname,argv);
>>             goto Lerror;
>>         }
>>
>>         while(1)
>>         {
>>             int status;
>>             pid_t wpid = waitpid(pid, &status, 0);
>>             if(exited(status))
>>             {
>>                 retval = exitstatus(status);
>>                 break;
>>             }
>>             else if(signaled(status))
>>             {
>>                 retval = -termsig(status);
>>                 break;
>>             }
>>             else if(stopped(status)) // ptrace support
>>                 continue;
>>             else
>>                 goto Lerror;
>>         }
>>
>>         return retval;
>>     }
>>
>> Lerror:
>>     retval = getErrno;
>>     error("Cannot spawn " ~ toString(pathname) ~ "; " ~ toString(strerror(retval)) ~ " [errno " ~ toString(retval) ~ "]", retval);
>>     return retval;
>> }
>>
>> extern(C)
>> {
>>     char* strerror(int);
>>     ushort getuid();
>> }
>> bool stopped(int status)    { return cast(bool)((status & 0xff) == 0x7f); }
>> bool signaled(int status)   { return cast(bool)((cast(char)((status & 0x7f) + 1) >> 1) > 0); }
>> int  termsig(int status)    { return status & 0x7f; }
>> bool exited(int status)     { return cast(bool)((status & 0x7f) == 0); }
>> int  exitstatus(int status) { return (status & 0xff00) >> 8; }
>>
>> } // version(linux)
>>
>> version (Windows)
>> {
>>   extern (Windows)
>>   {
>>     BOOL GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime,
>>                      LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime);
>>     BOOL GetUserNameA(LPTSTR lpBuffer, LPDWORD nSize);
>>   }
>>
>>   // fake struct stat
>>   struct struct_stat
>>   {
>>     ulong st_dev;
>>     uint st_ino;
>>     ulong st_mtime;
>>   }
>>
>>   // fake stat function
>>   int stat(char* name, struct_stat* st)
>>   {
>>     int retval = -1;
>>     HANDLE h = CreateFileA(name, FILE_GENERIC_READ, 0, null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, null);
>>     if (h != INVALID_HANDLE_VALUE)
>>     {
>>       FILETIME lastWriteTime;
>>       if (GetFileTime(h, null, null, &lastWriteTime))
>>       {
>>         st.st_mtime = lastWriteTime.dwHighDateTime;
>>         st.st_mtime <<= 32;
>>         st.st_mtime |= lastWriteTime.dwLowDateTime;
>>         retval = 0;
>>       }
>>       CloseHandle(h);
>>     }
>>     if(verbose)
>>     {
>>       fwritefln(stderr,"stat: ",toString(name)," : ",retval);
>>     }
>>     return retval;
>>   }
>>
>>   // fake getuid function
>>   char[] getuid()
>>   {
>>     char[] buffer;
>>     DWORD size = buffer.length = 64;
>>     if(GetUserNameA(buffer, &size))
>>     {
>>       buffer.length = size;
>>       return buffer;
>>     }
>>     return "";
>>   }
>>
>>   // fake chmod function
>>   void chmod(...)
>>   {
>>   }
>> }
> 



More information about the Digitalmars-d mailing list