www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Integrate rdmd into compiler?

reply Dave <Dave_member pathlink.com> writes:
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit


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).
Apr 24 2006
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
AFAIK it has been integrated; not sure when it happened.  Try `dmd -run
SCRIPT`.  As for gdc, I have no idea.

	-- Daniel

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(...)
   {
   }
 }

-- v1sw5+8Yhw5ln4+5pr6OFma8u6+7Lw4Tm6+7l6+7D a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Apr 25 2006
parent Dave <Dave_member pathlink.com> writes:
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(...)
   {
   }
 }


Apr 25 2006